diff --git a/internal/services/config/config.go b/internal/services/config/config.go index 5f5046f..5b243fe 100644 --- a/internal/services/config/config.go +++ b/internal/services/config/config.go @@ -120,6 +120,28 @@ type Executor struct { type InitImage struct { Image string `yaml:"image"` + + Auth *DockerRegistryAuth `yaml:"auth"` +} + +type DockerRegistryAuthType string + +const ( + DockerRegistryAuthTypeBasic DockerRegistryAuthType = "basic" + DockerRegistryAuthTypeEncodedAuth DockerRegistryAuthType = "encodedauth" +) + +type DockerRegistryAuth struct { + Type DockerRegistryAuthType `json:"type"` + + // basic auth + Username string `json:"username"` + Password string `json:"password"` + + // encoded auth string + Auth string `json:"auth"` + + // future auths like aws ecr auth } type Configstore struct { diff --git a/internal/services/executor/driver/docker.go b/internal/services/executor/driver/docker.go index 050fa2e..f0ee6bc 100644 --- a/internal/services/executor/driver/docker.go +++ b/internal/services/executor/driver/docker.go @@ -44,27 +44,29 @@ import ( ) type DockerDriver struct { - log *zap.SugaredLogger - client *client.Client - toolboxPath string - initImage string - executorID string - arch types.Arch + log *zap.SugaredLogger + client *client.Client + toolboxPath string + initImage string + initDockerConfig *registry.DockerConfig + executorID string + arch types.Arch } -func NewDockerDriver(logger *zap.Logger, executorID, toolboxPath, initImage string) (*DockerDriver, error) { +func NewDockerDriver(logger *zap.Logger, executorID, toolboxPath, initImage string, initDockerConfig *registry.DockerConfig) (*DockerDriver, error) { cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("1.26")) if err != nil { return nil, err } return &DockerDriver{ - log: logger.Sugar(), - client: cli, - toolboxPath: toolboxPath, - initImage: initImage, - executorID: executorID, - arch: types.ArchFromString(runtime.GOARCH), + log: logger.Sugar(), + client: cli, + toolboxPath: toolboxPath, + initImage: initImage, + initDockerConfig: initDockerConfig, + executorID: executorID, + arch: types.ArchFromString(runtime.GOARCH), }, nil } @@ -73,7 +75,7 @@ func (d *DockerDriver) Setup(ctx context.Context) error { } func (d *DockerDriver) createToolboxVolume(ctx context.Context, podID string, out io.Writer) (*dockertypes.Volume, error) { - if err := d.fetchImage(ctx, d.initImage, false, nil, out); err != nil { + if err := d.fetchImage(ctx, d.initImage, false, d.initDockerConfig, out); err != nil { return nil, err } diff --git a/internal/services/executor/driver/docker_test.go b/internal/services/executor/driver/docker_test.go index 3f2813f..aab230e 100644 --- a/internal/services/executor/driver/docker_test.go +++ b/internal/services/executor/driver/docker_test.go @@ -44,7 +44,7 @@ func TestDockerPod(t *testing.T) { initImage := "busybox:stable" - d, err := NewDockerDriver(logger, "executorid01", toolboxPath, initImage) + d, err := NewDockerDriver(logger, "executorid01", toolboxPath, initImage, nil) if err != nil { t.Fatalf("unexpected err: %v", err) } diff --git a/internal/services/executor/driver/k8s.go b/internal/services/executor/driver/k8s.go index ae0a6a3..37895c4 100644 --- a/internal/services/executor/driver/k8s.go +++ b/internal/services/executor/driver/k8s.go @@ -26,6 +26,7 @@ import ( "strings" "time" + "agola.io/agola/internal/services/executor/registry" "agola.io/agola/internal/util" "agola.io/agola/services/types" @@ -75,6 +76,7 @@ type K8sDriver struct { client *kubernetes.Clientset toolboxPath string initImage string + initDockerConfig *registry.DockerConfig namespace string executorID string executorsGroupID string @@ -96,7 +98,7 @@ type K8sPod struct { initVolumeDir string } -func NewK8sDriver(logger *zap.Logger, executorID, toolboxPath, initImage string) (*K8sDriver, error) { +func NewK8sDriver(logger *zap.Logger, executorID, toolboxPath, initImage string, initDockerConfig *registry.DockerConfig) (*K8sDriver, error) { kubeClientConfig := NewKubeClientConfig("", "", "") kubecfg, err := kubeClientConfig.ClientConfig() if err != nil { @@ -118,6 +120,7 @@ func NewK8sDriver(logger *zap.Logger, executorID, toolboxPath, initImage string) client: kubecli, toolboxPath: toolboxPath, initImage: initImage, + initDockerConfig: initDockerConfig, namespace: namespace, executorID: executorID, k8sLabelArch: corev1.LabelArchStable, @@ -324,13 +327,18 @@ func (d *K8sDriver) NewPod(ctx context.Context, podConfig *PodConfig, out io.Wri labels[executorIDKey] = d.executorID labels[executorsGroupIDKey] = d.executorsGroupID + // pod and secret name, based on pod id + name := podNamePrefix + podConfig.ID + dockerconfigj, err := json.Marshal(podConfig.DockerConfig) if err != nil { return nil, err } - // pod and secret name, based on pod id - name := podNamePrefix + podConfig.ID + initDockerconfigj, err := json.Marshal(d.initDockerConfig) + if err != nil { + return nil, err + } // secret that hold the docker registry auth secret := &corev1.Secret{ @@ -339,7 +347,7 @@ func (d *K8sDriver) NewPod(ctx context.Context, podConfig *PodConfig, out io.Wri Labels: labels, }, Data: map[string][]byte{ - ".dockerconfigjson": dockerconfigj, + ".dockerconfigjson": initDockerconfigj, }, Type: corev1.SecretTypeDockerConfigJson, } @@ -488,6 +496,28 @@ func (d *K8sDriver) NewPod(ctx context.Context, podConfig *PodConfig, out io.Wri fmt.Fprintf(out, "init container ready\n") + // Remove init container docker auth so it won't be used by user defined containers + dur := int64(0) + if err := secretClient.Delete(name, &metav1.DeleteOptions{GracePeriodSeconds: &dur}); err != nil { + return nil, err + } + + secret = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: labels, + }, + Data: map[string][]byte{ + ".dockerconfigjson": dockerconfigj, + }, + Type: corev1.SecretTypeDockerConfigJson, + } + + _, err = secretClient.Create(secret) + if err != nil { + return nil, err + } + coreclient, err := corev1client.NewForConfig(d.restconfig) if err != nil { return nil, err diff --git a/internal/services/executor/driver/k8s_test.go b/internal/services/executor/driver/k8s_test.go index 41d0859..142e599 100644 --- a/internal/services/executor/driver/k8s_test.go +++ b/internal/services/executor/driver/k8s_test.go @@ -43,7 +43,7 @@ func TestK8sPod(t *testing.T) { initImage := "busybox:stable" - d, err := NewK8sDriver(logger, "executorid01", toolboxPath, initImage) + d, err := NewK8sDriver(logger, "executorid01", toolboxPath, initImage, nil) if err != nil { t.Fatalf("unexpected err: %v", err) } diff --git a/internal/services/executor/executor.go b/internal/services/executor/executor.go index ef87423..3a41c65 100644 --- a/internal/services/executor/executor.go +++ b/internal/services/executor/executor.go @@ -1423,15 +1423,35 @@ func NewExecutor(ctx context.Context, l *zap.Logger, c *config.Executor) (*Execu e.listenAddress = fmt.Sprintf(":%s", port) + var initDockerConfig *registry.DockerConfig + + if e.c.InitImage.Auth != nil { + regName, err := registry.GetRegistry(e.c.InitImage.Image) + if err != nil { + return nil, err + } + dockerAuthConfig := + types.DockerRegistryAuth{ + Type: types.DockerRegistryAuthType(e.c.InitImage.Auth.Type), + Username: e.c.InitImage.Auth.Username, + Password: e.c.InitImage.Auth.Password, + Auth: e.c.InitImage.Auth.Auth, + } + initDockerConfig, err = registry.GenDockerConfig(map[string]types.DockerRegistryAuth{regName: dockerAuthConfig}, []string{e.c.InitImage.Image}) + if err != nil { + return nil, err + } + } + var d driver.Driver switch c.Driver.Type { case config.DriverTypeDocker: - d, err = driver.NewDockerDriver(logger, e.id, e.c.ToolboxPath, e.c.InitImage.Image) + d, err = driver.NewDockerDriver(logger, e.id, e.c.ToolboxPath, e.c.InitImage.Image, initDockerConfig) if err != nil { return nil, errors.Errorf("failed to create docker driver: %w", err) } case config.DriverTypeK8s: - d, err = driver.NewK8sDriver(logger, e.id, c.ToolboxPath, e.c.InitImage.Image) + d, err = driver.NewK8sDriver(logger, e.id, c.ToolboxPath, e.c.InitImage.Image, initDockerConfig) if err != nil { return nil, errors.Errorf("failed to create kubernetes driver: %w", err) }