|
Packit |
63bb0d |
package main
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
import (
|
|
Packit |
63bb0d |
"context"
|
|
Packit |
63bb0d |
"crypto/tls"
|
|
Packit |
63bb0d |
"crypto/x509"
|
|
Packit |
63bb0d |
"errors"
|
|
Packit |
63bb0d |
"flag"
|
|
Packit |
63bb0d |
"fmt"
|
|
Packit |
63bb0d |
"io/ioutil"
|
|
Packit |
63bb0d |
"log"
|
|
Packit |
63bb0d |
"os"
|
|
Packit |
63bb0d |
"path"
|
|
Packit |
63bb0d |
"time"
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
"github.com/BurntSushi/toml"
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
"github.com/osbuild/osbuild-composer/internal/upload/koji"
|
|
Packit |
63bb0d |
"github.com/osbuild/osbuild-composer/internal/worker"
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
const configFile = "/etc/osbuild-worker/osbuild-worker.toml"
|
|
Packit Service |
509fd4 |
|
|
Packit |
63bb0d |
type connectionConfig struct {
|
|
Packit |
63bb0d |
CACertFile string
|
|
Packit |
63bb0d |
ClientKeyFile string
|
|
Packit |
63bb0d |
ClientCertFile string
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
// Represents the implementation of a job type as defined by the worker API.
|
|
Packit Service |
509fd4 |
type JobImplementation interface {
|
|
Packit Service |
509fd4 |
Run(job worker.Job) error
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit |
63bb0d |
func createTLSConfig(config *connectionConfig) (*tls.Config, error) {
|
|
Packit |
63bb0d |
caCertPEM, err := ioutil.ReadFile(config.CACertFile)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return nil, err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
roots := x509.NewCertPool()
|
|
Packit |
63bb0d |
ok := roots.AppendCertsFromPEM(caCertPEM)
|
|
Packit |
63bb0d |
if !ok {
|
|
Packit |
63bb0d |
return nil, errors.New("failed to append root certificate")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
cert, err := tls.LoadX509KeyPair(config.ClientCertFile, config.ClientKeyFile)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return nil, err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return &tls.Config{
|
|
Packit |
63bb0d |
RootCAs: roots,
|
|
Packit |
63bb0d |
Certificates: []tls.Certificate{cert},
|
|
Packit |
63bb0d |
}, nil
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// Regularly ask osbuild-composer if the compose we're currently working on was
|
|
Packit |
63bb0d |
// canceled and exit the process if it was.
|
|
Packit |
63bb0d |
// It would be cleaner to kill the osbuild process using (`exec.CommandContext`
|
|
Packit |
63bb0d |
// or similar), but osbuild does not currently support this. Exiting here will
|
|
Packit |
63bb0d |
// make systemd clean up the whole cgroup and restart this service.
|
|
Packit Service |
509fd4 |
func WatchJob(ctx context.Context, job worker.Job) {
|
|
Packit |
63bb0d |
for {
|
|
Packit |
63bb0d |
select {
|
|
Packit |
63bb0d |
case <-time.After(15 * time.Second):
|
|
Packit Service |
509fd4 |
canceled, err := job.Canceled()
|
|
Packit Service |
509fd4 |
if err == nil && canceled {
|
|
Packit |
63bb0d |
log.Println("Job was canceled. Exiting.")
|
|
Packit |
63bb0d |
os.Exit(0)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
case <-ctx.Done():
|
|
Packit |
63bb0d |
return
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func main() {
|
|
Packit Service |
509fd4 |
var config struct {
|
|
Packit Service |
509fd4 |
KojiServers map[string]struct {
|
|
Packit Service |
509fd4 |
Kerberos *struct {
|
|
Packit Service |
509fd4 |
Principal string `toml:"principal"`
|
|
Packit Service |
509fd4 |
KeyTab string `toml:"keytab"`
|
|
Packit Service |
509fd4 |
} `toml:"kerberos,omitempty"`
|
|
Packit Service |
509fd4 |
} `toml:"koji"`
|
|
Packit Service |
509fd4 |
}
|
|
Packit |
63bb0d |
var unix bool
|
|
Packit |
63bb0d |
flag.BoolVar(&unix, "unix", false, "Interpret 'address' as a path to a unix domain socket instead of a network address")
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
flag.Usage = func() {
|
|
Packit |
63bb0d |
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [-unix] address\n", os.Args[0])
|
|
Packit |
63bb0d |
flag.PrintDefaults()
|
|
Packit |
63bb0d |
os.Exit(0)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
flag.Parse()
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
address := flag.Arg(0)
|
|
Packit |
63bb0d |
if address == "" {
|
|
Packit |
63bb0d |
flag.Usage()
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
_, err := toml.DecodeFile(configFile, &config)
|
|
Packit Service |
509fd4 |
if err == nil {
|
|
Packit Service |
509fd4 |
log.Println("Composer configuration:")
|
|
Packit Service |
509fd4 |
encoder := toml.NewEncoder(log.Writer())
|
|
Packit Service |
509fd4 |
err := encoder.Encode(&config)
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
log.Fatalf("Could not print config: %v", err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
} else if !os.IsNotExist(err) {
|
|
Packit Service |
509fd4 |
log.Fatalf("Could not load config file '%s': %v", configFile, err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit |
63bb0d |
cacheDirectory, ok := os.LookupEnv("CACHE_DIRECTORY")
|
|
Packit |
63bb0d |
if !ok {
|
|
Packit |
63bb0d |
log.Fatal("CACHE_DIRECTORY is not set. Is the service file missing CacheDirectory=?")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
store := path.Join(cacheDirectory, "osbuild-store")
|
|
Packit Service |
bcdfb1 |
output := path.Join(cacheDirectory, "output")
|
|
Packit Service |
bcdfb1 |
_ = os.Mkdir(output, os.ModeDir)
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
kojiServers := make(map[string]koji.GSSAPICredentials)
|
|
Packit Service |
509fd4 |
for server, creds := range config.KojiServers {
|
|
Packit Service |
509fd4 |
if creds.Kerberos == nil {
|
|
Packit Service |
509fd4 |
// For now we only support Kerberos authentication.
|
|
Packit Service |
509fd4 |
continue
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
kojiServers[server] = koji.GSSAPICredentials{
|
|
Packit Service |
509fd4 |
Principal: creds.Kerberos.Principal,
|
|
Packit Service |
509fd4 |
KeyTab: creds.Kerberos.KeyTab,
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit |
63bb0d |
var client *worker.Client
|
|
Packit |
63bb0d |
if unix {
|
|
Packit |
63bb0d |
client = worker.NewClientUnix(address)
|
|
Packit |
63bb0d |
} else {
|
|
Packit |
63bb0d |
conf, err := createTLSConfig(&connectionConfig{
|
|
Packit |
63bb0d |
CACertFile: "/etc/osbuild-composer/ca-crt.pem",
|
|
Packit |
63bb0d |
ClientKeyFile: "/etc/osbuild-composer/worker-key.pem",
|
|
Packit |
63bb0d |
ClientCertFile: "/etc/osbuild-composer/worker-crt.pem",
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
log.Fatalf("Error creating TLS config: %v", err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
client, err = worker.NewClient("https://"+address, conf)
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
log.Fatalf("Error creating worker client: %v", err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
jobImpls := map[string]JobImplementation{
|
|
Packit Service |
509fd4 |
"osbuild": &OSBuildJobImpl{
|
|
Packit Service |
509fd4 |
Store: store,
|
|
Packit Service |
bcdfb1 |
Output: output,
|
|
Packit Service |
509fd4 |
KojiServers: kojiServers,
|
|
Packit Service |
509fd4 |
},
|
|
Packit Service |
509fd4 |
"osbuild-koji": &OSBuildKojiJobImpl{
|
|
Packit Service |
509fd4 |
Store: store,
|
|
Packit Service |
bcdfb1 |
Output: output,
|
|
Packit Service |
509fd4 |
KojiServers: kojiServers,
|
|
Packit Service |
509fd4 |
},
|
|
Packit Service |
509fd4 |
"koji-init": &KojiInitJobImpl{
|
|
Packit Service |
509fd4 |
KojiServers: kojiServers,
|
|
Packit Service |
509fd4 |
},
|
|
Packit Service |
509fd4 |
"koji-finalize": &KojiFinalizeJobImpl{
|
|
Packit Service |
509fd4 |
KojiServers: kojiServers,
|
|
Packit Service |
509fd4 |
},
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
acceptedJobTypes := []string{}
|
|
Packit Service |
509fd4 |
for jt := range jobImpls {
|
|
Packit Service |
509fd4 |
acceptedJobTypes = append(acceptedJobTypes, jt)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
for {
|
|
Packit |
63bb0d |
fmt.Println("Waiting for a new job...")
|
|
Packit Service |
509fd4 |
job, err := client.RequestJob(acceptedJobTypes)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
log.Fatal(err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
impl, exists := jobImpls[job.Type()]
|
|
Packit Service |
509fd4 |
if !exists {
|
|
Packit Service |
509fd4 |
log.Printf("Ignoring job with unknown type %s", job.Type())
|
|
Packit Service |
509fd4 |
continue
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
fmt.Printf("Running '%s' job %v\n", job.Type(), job.Id())
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
ctx, cancelWatcher := context.WithCancel(context.Background())
|
|
Packit Service |
509fd4 |
go WatchJob(ctx, job)
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
err = impl.Run(job)
|
|
Packit Service |
509fd4 |
cancelWatcher()
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit Service |
509fd4 |
log.Printf("Job %s failed: %v", job.Id(), err)
|
|
Packit Service |
509fd4 |
continue
|
|
Packit |
63bb0d |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
log.Printf("Job %s finished", job.Id())
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|