]>
git.ipfire.org Git - pbs.git/blob - src/buildservice/__init__.py
14 from . import bugtracker
15 from . import builders
18 from . import database
19 from . import distribution
21 from . import jobqueue
25 from . import messages
27 from . import packages
28 from . import repository
29 from . import settings
30 from . import sessions
36 log
= logging
.getLogger("backend")
40 from .__version
__ import VERSION
as __version__
42 from .decorators
import *
43 from .constants
import *
45 class Backend(object):
48 # A list of any background tasks
51 def __init__(self
, config_file
=None):
52 # Read configuration file.
53 self
.config
= self
.read_config(config_file
)
55 # Global pakfire settings (from database).
56 self
.settings
= settings
.Settings(self
)
58 self
.aws
= aws
.AWS(self
)
59 self
.builds
= builds
.Builds(self
)
60 self
.cache
= cache
.Cache(self
)
61 self
.jobs
= jobs
.Jobs(self
)
62 self
.builders
= builders
.Builders(self
)
63 self
.distros
= distribution
.Distributions(self
)
64 self
.events
= events
.Events(self
)
65 self
.jobqueue
= jobqueue
.JobQueue(self
)
66 self
.keys
= keys
.Keys(self
)
67 self
.messages
= messages
.Messages(self
)
68 self
.mirrors
= mirrors
.Mirrors(self
)
69 self
.packages
= packages
.Packages(self
)
70 self
.repos
= repository
.Repositories(self
)
71 self
.sessions
= sessions
.Sessions(self
)
72 self
.sources
= sources
.Sources(self
)
73 self
.updates
= updates
.Updates(self
)
74 self
.uploads
= uploads
.Uploads(self
)
75 self
.users
= users
.Users(self
)
77 # Open a connection to bugzilla.
78 self
.bugzilla
= bugtracker
.Bugzilla(self
)
81 def _environment_configuration(self
):
84 # Get database configuration
86 "name" : os
.environ
.get("PBS_DATABASE_NAME"),
87 "hostname" : os
.environ
.get("PBS_DATABASE_HOSTNAME"),
88 "user" : os
.environ
.get("PBS_DATABASE_USER"),
89 "password" : os
.environ
.get("PBS_DATABASE_PASSWORD"),
94 def read_config(self
, path
):
95 c
= configparser
.SafeConfigParser()
97 # Import configuration from environment
98 for section
in self
._environment
_configuration
:
99 c
.add_section(section
)
101 for k
in self
._environment
_configuration
[section
]:
102 c
.set(section
, k
, self
._environment
_configuration
[section
][k
] or "")
104 # Load default configuration file first
106 os
.path
.join(CONFIGSDIR
, "pbs.conf"),
112 # Load all configuration files
114 if os
.path
.exists(path
):
115 log
.debug("Loading configuration from %s" % path
)
118 log
.error("No such file %s" % path
)
125 name
= self
.config
.get("database", "name")
126 hostname
= self
.config
.get("database", "hostname")
127 user
= self
.config
.get("database", "user")
128 password
= self
.config
.get("database", "password")
129 except configparser
.Error
as e
:
130 log
.error("Error parsing the config: %s" % e
.message
)
132 log
.debug("Connecting to database %s @ %s" % (name
, hostname
))
134 return database
.Connection(hostname
, name
, user
=user
, password
=password
)
136 def path_to_url(self
, path
):
138 Takes a path to a file on the file system and converts it into a URL
141 baseurl
= self
.settings
.get("baseurl")
145 "files", os
.path
.relpath(path
, PAKFIRE_DIR
),
148 # Join it all together
149 return urllib
.parse
.urljoin(baseurl
, path
)
151 def pakfire(self
, config
, offline
=True, **kwargs
):
153 Launches a new Pakfire instance with the given configuration
155 log
.debug("Launching pakfire with configuration:\n%s" % config
)
157 # Write configuration to file
158 t
= self
._write
_tempfile
(config
)
160 # Launch a new Pakfire instance
162 return pakfire
.Pakfire(conf
=t
, logger
=log
.log
, offline
=offline
, **kwargs
)
165 # Delete the configuration file
168 # Functions to run something in background
170 def run_task(self
, callback
, *args
):
172 Runs the given coroutine in the background
175 task
= asyncio
.create_task(callback(*args
))
177 # Keep a reference to the task and remove it when the task has finished
178 self
.__tasks
.add(task
)
179 task
.add_done_callback(self
.__tasks
.discard
)
183 def run_periodic_task(self
, delay
, callback
, *args
):
185 Calls the given callback periodically in the background
187 self
.run_task(self
._periodic
_task
, delay
, callback
, *args
)
189 async def _periodic_task(self
, delay
, callback
, *args
):
191 Helper function for run_periodic_task() that will call the given
192 callback regulary after the timer has expired.
194 log
.debug("Periodic callback %r started" % callback
)
197 # Wait a little moment
198 await asyncio
.sleep(delay
)
201 ret
= callback(*args
)
203 # Await ret if callback is a coroutine
204 if inspect
.isawaitable(ret
):
207 except Exception as e
:
208 log
.error("Exception in periodic callback %r" % callback
, exc_info
=True)
212 async def command(self
, *args
, krb5_auth
=False, **kwargs
):
214 Runs this shell command
216 # Authenticate using Kerberos
218 await self
.krb5_auth()
220 log
.debug("Running command: %s" % " ".join(args
))
223 process
= await asyncio
.create_subprocess_exec(
225 stdin
=asyncio
.subprocess
.DEVNULL
,
226 stdout
=asyncio
.subprocess
.PIPE
,
227 stderr
=asyncio
.subprocess
.STDOUT
,
231 # Fetch output of command and send it to the logger
233 line
= await process
.stdout
.readline()
245 # Wait until the process has finished
248 async def krb5_auth(self
):
249 log
.debug("Performing Kerberos authentication...")
251 # Fetch path to keytab
252 keytab
= self
.settings
.get("krb5-keytab")
254 log
.warning("No keytab configured")
257 # Fetch Kerberos principal
258 principal
= self
.settings
.get("krb5-principal")
260 log
.warning("No Kerberos principal configured")
263 # Fetch a Kerberos ticket
265 "kinit", "-k", "-t", keytab
, principal
,
268 async def copy(self
, src
, dst
):
270 Copies a file from src to dst
272 log
.debug("Copying %s to %s" % (src
, dst
))
274 path
= os
.path
.dirname(dst
)
276 # Create destination path (if it does not exist)
278 await asyncio
.to_thread(os
.makedirs
, path
)
279 except FileExistsError
:
282 # Copy data without any metadata
283 await asyncio
.to_thread(shutil
.copyfile
, src
, dst
)
285 async def unlink(self
, path
):
289 log
.debug("Unlinking %s" % path
)
292 await asyncio
.to_thread(os
.unlink
, path
)
296 def _write_tempfile(self
, content
):
298 Writes the content to a temporary file and returns its path
300 t
= tempfile
.NamedTemporaryFile(delete
=False)
303 t
.write(content
.encode())
308 async def open(self
, path
):
310 Opens a package and returns the archive
312 return await asyncio
.to_thread(self
._open
, path
)
314 def _open(self
, path
):
315 # Create a dummy Pakfire instance
316 p
= pakfire
.Pakfire(offline
=True)
321 async def cleanup(self
):
323 Called regularly to cleanup any left-over resources
326 await self
.sessions
.cleanup()
329 await self
.uploads
.cleanup()
331 async def sync(self
):
333 Syncs any repository that should be mirrored
335 log
.info("Syncing mirrors...")
337 # Fetch the sync target
338 target
= self
.settings
.get("sync-target")
340 log
.warning("No sync target configured")
346 # Show what is being transferred
349 # Compress any transferred data
352 # Enable archive mode
355 # Preserve hardlinks, ACLs & XATTRs
360 # Delete any files that we have deleted
364 # Remove any empty directories
365 "--prune-empty-dirs",
367 # Make the transaction atomic
370 # Add source & target
375 # Add all mirrored repositories
376 for repo
in self
.repos
.mirrored
:
377 path
= os
.path
.relpath(repo
.local_path(), PAKFIRE_DIR
)
379 commandline
.append("--include=%s***" % path
)
381 # Exclude everything that hasn't been included
382 commandline
+= ("--include=*/", "--exclude=*")
385 await self
.command(*commandline
, krb5_auth
=True)