diff --git a/internal/pkg/plugin/jenkinspipeline/create.go b/internal/pkg/plugin/jenkinspipeline/create.go index b1e431cb4..8563a9fa7 100644 --- a/internal/pkg/plugin/jenkinspipeline/create.go +++ b/internal/pkg/plugin/jenkinspipeline/create.go @@ -11,6 +11,7 @@ func Create(options map[string]interface{}) (map[string]interface{}, error) { // Initialize Operator with Operations operator := &plugininstaller.Operator{ PreExecuteOperations: plugininstaller.PreExecuteOperations{ + jenkins.SetHarborAuth, jenkins.SetJobDefaultConfig, jenkins.ValidateJobConfig, }, diff --git a/internal/pkg/plugin/jenkinspipeline/delete.go b/internal/pkg/plugin/jenkinspipeline/delete.go index 2acf6d44b..5f98fdf32 100644 --- a/internal/pkg/plugin/jenkinspipeline/delete.go +++ b/internal/pkg/plugin/jenkinspipeline/delete.go @@ -14,6 +14,7 @@ func Delete(options map[string]interface{}) (bool, error) { jenkins.ValidateJobConfig, }, ExecuteOperations: plugininstaller.ExecuteOperations{ + // TODO(daniel-hutao): delete secret: docker-config ci.DeleteCIFiles, jenkins.DeleteJob, }, diff --git a/internal/pkg/plugininstaller/jenkins/option.go b/internal/pkg/plugininstaller/jenkins/option.go index 2c35f9755..ae4f563e1 100644 --- a/internal/pkg/plugininstaller/jenkins/option.go +++ b/internal/pkg/plugininstaller/jenkins/option.go @@ -31,6 +31,10 @@ type JobOptions struct { ProjectBranch string `mapstructure:"projectBranch"` JenkinsfilePath string `mapstructure:"jenkinsfilePath" validate:"required"` + JenkinsNamespace string `mapstructure:"jenkinsNamespace"` + HarborURL string `mapstructure:"harborURL" validate:"url"` + HarborUser string `mapstructure:"harborUser"` + JenkinsEnableRestart bool `mapstructure:"jenkinsEnableRestart"` // used in package @@ -55,6 +59,10 @@ func newJobOptions(options plugininstaller.RawOptions) (*JobOptions, error) { if err := mapstructure.Decode(options, &opts); err != nil { return nil, err } + // TODO(daniel-hutao): wait for refactoring + if opts.JenkinsNamespace == "" { + opts.JenkinsNamespace = "jenkins" + } return &opts, nil } diff --git a/internal/pkg/plugininstaller/jenkins/validate.go b/internal/pkg/plugininstaller/jenkins/validate.go index 7e8e68ad2..9e8d88b06 100644 --- a/internal/pkg/plugininstaller/jenkins/validate.go +++ b/internal/pkg/plugininstaller/jenkins/validate.go @@ -1,6 +1,7 @@ package jenkins import ( + "encoding/base64" "errors" "fmt" "math/rand" @@ -13,6 +14,7 @@ import ( "github.com/devstream-io/devstream/internal/pkg/plugininstaller/common" "github.com/devstream-io/devstream/pkg/util/jenkins" "github.com/devstream-io/devstream/pkg/util/k8s" + "github.com/devstream-io/devstream/pkg/util/log" "github.com/devstream-io/devstream/pkg/util/validator" ) @@ -126,3 +128,51 @@ func buildCIConfig(jenkinsFilePath string) *ci.CIConfig { } return ciConfig } + +func SetHarborAuth(options plugininstaller.RawOptions) (plugininstaller.RawOptions, error) { + opts, err := newJobOptions(options) + if err != nil { + return nil, err + } + namespace := opts.JenkinsNamespace + harborURL := opts.HarborURL + harborUser := opts.HarborUser + + harborPasswd := os.Getenv("HARBOR_PASSWORD") + if harborPasswd == "" { + return nil, fmt.Errorf("the environment variable HARBOR_PASSWORD is not set") + } + + return options, createDockerSecret(namespace, harborURL, harborUser, harborPasswd) +} + +func createDockerSecret(namespace, url, username, password string) error { + tmpStr := fmt.Sprintf("%s:%s", username, password) + authStr := base64.StdEncoding.EncodeToString([]byte(tmpStr)) + log.Debugf("Auth string: %s.", authStr) + + configJsonStrTpl := `{ +"auths": { + "%s": { + "auth": "%s" + } +}` + configJsonStr := fmt.Sprintf(configJsonStrTpl, url, authStr) + log.Debugf("config.json: %s.", configJsonStr) + + // create secret in k8s + client, err := k8s.NewClient() + if err != nil { + return err + } + + data := map[string][]byte{ + "config.json": []byte(configJsonStr), + } + _, err = client.ApplySecret("docker-config", namespace, data, nil) + if err != nil { + return err + } + log.Infof("Secret %s/%s has been created.", namespace, "docker-config") + return nil +} diff --git a/internal/pkg/show/config/plugins/jenkins-pipeline.yaml b/internal/pkg/show/config/plugins/jenkins-pipeline.yaml index 617a9cfe8..beb1f22cd 100644 --- a/internal/pkg/show/config/plugins/jenkins-pipeline.yaml +++ b/internal/pkg/show/config/plugins/jenkins-pipeline.yaml @@ -21,3 +21,9 @@ tools: jenkinsfilePath: https://raw.githubusercontent.com/YOUR_CI_FILE_LOCATION # projectURL is the project repo location, like http://127.0.0.1:30080/root/project projectURL: http://127.0.0.1:30080/root/test-project + # jenkins namespace in k8s cluster + jenkinsNamespace: jenkins + # harbor URL for pulling/pushing + harborURL: http://harbor.example.com:80 + # harbor username + harborUser: admin diff --git a/pkg/util/k8s/secret.go b/pkg/util/k8s/secret.go index 65f600d45..89ab93bee 100644 --- a/pkg/util/k8s/secret.go +++ b/pkg/util/k8s/secret.go @@ -3,7 +3,9 @@ package k8s import ( "context" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/client-go/applyconfigurations/core/v1" ) func (c *Client) GetSecret(namespace, name string) (map[string]string, error) { @@ -18,3 +20,18 @@ func (c *Client) GetSecret(namespace, name string) (map[string]string, error) { } return secretMap, nil } + +func (c *Client) ApplySecret(name, namespace string, data map[string][]byte, labels map[string]string) (*corev1.Secret, error) { + secret := v1.Secret(name, namespace). + WithLabels(labels). + WithData(data). + WithImmutable(false) + + applyOptions := metav1.ApplyOptions{ + FieldManager: "DevStream", + } + + return c.clientset.CoreV1(). + Secrets(namespace). + Apply(context.Background(), secret, applyOptions) +}