mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-09-03 02:28:58 +00:00
Flexible image pull secret reference (#3016)
Co-authored-by: pat-s <patrick.schratz@gmail.com>
This commit is contained in:
@@ -46,6 +46,10 @@ agent:
|
|||||||
|
|
||||||
Determines if containers must be required to run as non-root users.
|
Determines if containers must be required to run as non-root users.
|
||||||
|
|
||||||
|
- `WOODPECKER_BACKEND_K8S_PULL_SECRET_NAMES` (default: empty)
|
||||||
|
|
||||||
|
Secret names to pull images from private repositories.
|
||||||
|
|
||||||
## Job specific configuration
|
## Job specific configuration
|
||||||
|
|
||||||
### Resources
|
### Resources
|
||||||
|
@@ -7,6 +7,7 @@ Some versions need some changes to the server configuration or the pipeline conf
|
|||||||
- Deprecated `steps.[name].group` in favor of `steps.[name].depends_on` (see [workflow syntax](./20-usage/20-workflow-syntax.md#depends_on) to learn how to set dependencies)
|
- Deprecated `steps.[name].group` in favor of `steps.[name].depends_on` (see [workflow syntax](./20-usage/20-workflow-syntax.md#depends_on) to learn how to set dependencies)
|
||||||
- Removed `WOODPECKER_ROOT_PATH` and `WOODPECKER_ROOT_URL` config variables. Use `WOODPECKER_HOST` with a path instead
|
- Removed `WOODPECKER_ROOT_PATH` and `WOODPECKER_ROOT_URL` config variables. Use `WOODPECKER_HOST` with a path instead
|
||||||
- Pipelines without a config file will now be skipped instead of failing
|
- Pipelines without a config file will now be skipped instead of failing
|
||||||
|
- Deprecated implicitly defined `regcred` image pull secret. Set it explicitly via `WOODPECKER_BACKEND_K8S_PULL_SECRET_NAMES`.
|
||||||
|
|
||||||
## 2.0.0
|
## 2.0.0
|
||||||
|
|
||||||
|
@@ -74,4 +74,10 @@ var Flags = []cli.Flag{
|
|||||||
Usage: "duration to wait before retrying to connect to the server",
|
Usage: "duration to wait before retrying to connect to the server",
|
||||||
Value: time.Second * 2,
|
Value: time.Second * 2,
|
||||||
},
|
},
|
||||||
|
&cli.StringSliceFlag{
|
||||||
|
EnvVars: []string{"WOODPECKER_BACKEND_K8S_PULL_SECRET_NAMES"},
|
||||||
|
Name: "backend-k8s-pod-image-pull-secret-names",
|
||||||
|
Usage: "backend k8s pull secret names for private registries",
|
||||||
|
Value: cli.NewStringSlice("regcred"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
@@ -54,13 +54,14 @@ type kube struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
Namespace string
|
Namespace string
|
||||||
StorageClass string
|
StorageClass string
|
||||||
VolumeSize string
|
VolumeSize string
|
||||||
StorageRwx bool
|
StorageRwx bool
|
||||||
PodLabels map[string]string
|
PodLabels map[string]string
|
||||||
PodAnnotations map[string]string
|
PodAnnotations map[string]string
|
||||||
SecurityContext SecurityContextConfig
|
ImagePullSecretNames []string
|
||||||
|
SecurityContext SecurityContextConfig
|
||||||
}
|
}
|
||||||
type SecurityContextConfig struct {
|
type SecurityContextConfig struct {
|
||||||
RunAsNonRoot bool
|
RunAsNonRoot bool
|
||||||
@@ -80,16 +81,21 @@ func configFromCliContext(ctx context.Context) (*config, error) {
|
|||||||
if ctx != nil {
|
if ctx != nil {
|
||||||
if c, ok := ctx.Value(types.CliContext).(*cli.Context); ok {
|
if c, ok := ctx.Value(types.CliContext).(*cli.Context); ok {
|
||||||
config := config{
|
config := config{
|
||||||
Namespace: c.String("backend-k8s-namespace"),
|
Namespace: c.String("backend-k8s-namespace"),
|
||||||
StorageClass: c.String("backend-k8s-storage-class"),
|
StorageClass: c.String("backend-k8s-storage-class"),
|
||||||
VolumeSize: c.String("backend-k8s-volume-size"),
|
VolumeSize: c.String("backend-k8s-volume-size"),
|
||||||
StorageRwx: c.Bool("backend-k8s-storage-rwx"),
|
StorageRwx: c.Bool("backend-k8s-storage-rwx"),
|
||||||
PodLabels: make(map[string]string), // just init empty map to prevent nil panic
|
PodLabels: make(map[string]string), // just init empty map to prevent nil panic
|
||||||
PodAnnotations: make(map[string]string), // just init empty map to prevent nil panic
|
PodAnnotations: make(map[string]string), // just init empty map to prevent nil panic
|
||||||
|
ImagePullSecretNames: c.StringSlice("backend-k8s-pod-image-pull-secret-names"),
|
||||||
SecurityContext: SecurityContextConfig{
|
SecurityContext: SecurityContextConfig{
|
||||||
RunAsNonRoot: c.Bool("backend-k8s-secctx-nonroot"),
|
RunAsNonRoot: c.Bool("backend-k8s-secctx-nonroot"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
// TODO: remove in next major
|
||||||
|
if len(config.ImagePullSecretNames) == 1 && config.ImagePullSecretNames[0] == "regcred" {
|
||||||
|
log.Warn().Msg("WOODPECKER_BACKEND_K8S_PULL_SECRET_NAMES is set to the default ('regcred'). It will default to empty in Woodpecker 3.0. Set it explicitly before then.")
|
||||||
|
}
|
||||||
// Unmarshal label and annotation settings here to ensure they're valid on startup
|
// Unmarshal label and annotation settings here to ensure they're valid on startup
|
||||||
if labels := c.String("backend-k8s-pod-labels"); labels != "" {
|
if labels := c.String("backend-k8s-pod-labels"); labels != "" {
|
||||||
if err := yaml.Unmarshal([]byte(labels), &config.PodLabels); err != nil {
|
if err := yaml.Unmarshal([]byte(labels), &config.PodLabels); err != nil {
|
||||||
|
@@ -36,7 +36,7 @@ const (
|
|||||||
|
|
||||||
func mkPod(namespace, name, image, workDir, goos, serviceAccountName string,
|
func mkPod(namespace, name, image, workDir, goos, serviceAccountName string,
|
||||||
pool, privileged bool,
|
pool, privileged bool,
|
||||||
commands, vols []string,
|
commands, vols, pullSecretNames []string,
|
||||||
labels, annotations, env, nodeSelector map[string]string,
|
labels, annotations, env, nodeSelector map[string]string,
|
||||||
extraHosts []types.HostAlias, tolerations []types.Toleration, resources types.Resources,
|
extraHosts []types.HostAlias, tolerations []types.Toleration, resources types.Resources,
|
||||||
securityContext *types.SecurityContext, securityContextConfig SecurityContextConfig,
|
securityContext *types.SecurityContext, securityContextConfig SecurityContextConfig,
|
||||||
@@ -45,7 +45,8 @@ func mkPod(namespace, name, image, workDir, goos, serviceAccountName string,
|
|||||||
|
|
||||||
meta := podMeta(name, namespace, labels, annotations)
|
meta := podMeta(name, namespace, labels, annotations)
|
||||||
|
|
||||||
spec, err := podSpec(serviceAccountName, vols, env, nodeSelector, extraHosts, tolerations, securityContext, securityContextConfig)
|
spec, err := podSpec(serviceAccountName, vols, pullSecretNames, env, nodeSelector, extraHosts, tolerations,
|
||||||
|
securityContext, securityContextConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -85,7 +86,7 @@ func podMeta(name, namespace string, labels, annotations map[string]string) meta
|
|||||||
return meta
|
return meta
|
||||||
}
|
}
|
||||||
|
|
||||||
func podSpec(serviceAccountName string, vols []string, env, backendNodeSelector map[string]string,
|
func podSpec(serviceAccountName string, vols, pullSecretNames []string, env, backendNodeSelector map[string]string,
|
||||||
extraHosts []types.HostAlias, backendTolerations []types.Toleration,
|
extraHosts []types.HostAlias, backendTolerations []types.Toleration,
|
||||||
securityContext *types.SecurityContext, securityContextConfig SecurityContextConfig,
|
securityContext *types.SecurityContext, securityContextConfig SecurityContextConfig,
|
||||||
) (v1.PodSpec, error) {
|
) (v1.PodSpec, error) {
|
||||||
@@ -93,7 +94,7 @@ func podSpec(serviceAccountName string, vols []string, env, backendNodeSelector
|
|||||||
spec := v1.PodSpec{
|
spec := v1.PodSpec{
|
||||||
RestartPolicy: v1.RestartPolicyNever,
|
RestartPolicy: v1.RestartPolicyNever,
|
||||||
ServiceAccountName: serviceAccountName,
|
ServiceAccountName: serviceAccountName,
|
||||||
ImagePullSecrets: []v1.LocalObjectReference{{Name: "regcred"}},
|
ImagePullSecrets: imagePullSecretsReferences(pullSecretNames),
|
||||||
}
|
}
|
||||||
|
|
||||||
spec.HostAliases = hostAliases(extraHosts)
|
spec.HostAliases = hostAliases(extraHosts)
|
||||||
@@ -211,6 +212,22 @@ func hostAlias(extraHost types.HostAlias) v1.HostAlias {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func imagePullSecretsReferences(imagePullSecretNames []string) []v1.LocalObjectReference {
|
||||||
|
log.Trace().Msgf("Using the image pull secrets: %v", imagePullSecretNames)
|
||||||
|
|
||||||
|
secretReferences := make([]v1.LocalObjectReference, len(imagePullSecretNames))
|
||||||
|
for i, imagePullSecretName := range imagePullSecretNames {
|
||||||
|
secretReferences[i] = imagePullSecretsReference(imagePullSecretName)
|
||||||
|
}
|
||||||
|
return secretReferences
|
||||||
|
}
|
||||||
|
|
||||||
|
func imagePullSecretsReference(imagePullSecretName string) v1.LocalObjectReference {
|
||||||
|
return v1.LocalObjectReference{
|
||||||
|
Name: imagePullSecretName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func resourceRequirements(resources types.Resources) (v1.ResourceRequirements, error) {
|
func resourceRequirements(resources types.Resources) (v1.ResourceRequirements, error) {
|
||||||
var err error
|
var err error
|
||||||
requirements := v1.ResourceRequirements{}
|
requirements := v1.ResourceRequirements{}
|
||||||
@@ -357,7 +374,7 @@ func startPod(ctx context.Context, engine *kube, step *types.Step) (*v1.Pod, err
|
|||||||
|
|
||||||
pod, err := mkPod(engine.config.Namespace, podName, step.Image, step.WorkingDir, engine.goos, step.BackendOptions.Kubernetes.ServiceAccountName,
|
pod, err := mkPod(engine.config.Namespace, podName, step.Image, step.WorkingDir, engine.goos, step.BackendOptions.Kubernetes.ServiceAccountName,
|
||||||
step.Pull, step.Privileged,
|
step.Pull, step.Privileged,
|
||||||
step.Commands, step.Volumes,
|
step.Commands, step.Volumes, engine.config.ImagePullSecretNames,
|
||||||
engine.config.PodLabels, engine.config.PodAnnotations, step.Environment, step.BackendOptions.Kubernetes.NodeSelector,
|
engine.config.PodLabels, engine.config.PodAnnotations, step.Environment, step.BackendOptions.Kubernetes.NodeSelector,
|
||||||
step.ExtraHosts, step.BackendOptions.Kubernetes.Tolerations, step.BackendOptions.Kubernetes.Resources, step.BackendOptions.Kubernetes.SecurityContext, engine.config.SecurityContext)
|
step.ExtraHosts, step.BackendOptions.Kubernetes.Tolerations, step.BackendOptions.Kubernetes.Resources, step.BackendOptions.Kubernetes.SecurityContext, engine.config.SecurityContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -96,19 +96,14 @@ func TestTinyPod(t *testing.T) {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"restartPolicy": "Never",
|
"restartPolicy": "Never"
|
||||||
"imagePullSecrets": [
|
|
||||||
{
|
|
||||||
"name": "regcred"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"status": {}
|
"status": {}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
pod, err := mkPod("woodpecker", "wp-01he8bebctabr3kgk0qj36d2me-0", "gradle:8.4.0-jdk21", "/woodpecker/src", "linux/amd64", "",
|
pod, err := mkPod("woodpecker", "wp-01he8bebctabr3kgk0qj36d2me-0", "gradle:8.4.0-jdk21", "/woodpecker/src", "linux/amd64", "",
|
||||||
false, false,
|
false, false,
|
||||||
[]string{"gradle build"}, []string{"workspace:/woodpecker/src"},
|
[]string{"gradle build"}, []string{"workspace:/woodpecker/src"}, nil,
|
||||||
nil, nil, map[string]string{"CI": "woodpecker"}, nil,
|
nil, nil, map[string]string{"CI": "woodpecker"}, nil,
|
||||||
nil, nil,
|
nil, nil,
|
||||||
types.Resources{Requests: nil, Limits: nil}, nil, SecurityContextConfig{},
|
types.Resources{Requests: nil, Limits: nil}, nil, SecurityContextConfig{},
|
||||||
@@ -213,6 +208,9 @@ func TestFullPod(t *testing.T) {
|
|||||||
"imagePullSecrets": [
|
"imagePullSecrets": [
|
||||||
{
|
{
|
||||||
"name": "regcred"
|
"name": "regcred"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "another-pull-secret"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tolerations": [
|
"tolerations": [
|
||||||
@@ -246,7 +244,7 @@ func TestFullPod(t *testing.T) {
|
|||||||
}
|
}
|
||||||
pod, err := mkPod("woodpecker", "wp-01he8bebctabr3kgk0qj36d2me-0", "meltwater/drone-cache", "/woodpecker/src", "linux/amd64", "wp-svc-acc",
|
pod, err := mkPod("woodpecker", "wp-01he8bebctabr3kgk0qj36d2me-0", "meltwater/drone-cache", "/woodpecker/src", "linux/amd64", "wp-svc-acc",
|
||||||
true, true,
|
true, true,
|
||||||
[]string{"go get", "go test"}, []string{"woodpecker-cache:/woodpecker/src/cache"},
|
[]string{"go get", "go test"}, []string{"woodpecker-cache:/woodpecker/src/cache"}, []string{"regcred", "another-pull-secret"},
|
||||||
map[string]string{"app": "test"}, map[string]string{"apparmor.security": "runtime/default"}, map[string]string{"CGO": "0"}, map[string]string{"storage": "ssd"},
|
map[string]string{"app": "test"}, map[string]string{"apparmor.security": "runtime/default"}, map[string]string{"CGO": "0"}, map[string]string{"storage": "ssd"},
|
||||||
hostAliases, []types.Toleration{{Key: "net-port", Value: "100Mbit", Effect: types.TaintEffectNoSchedule}},
|
hostAliases, []types.Toleration{{Key: "net-port", Value: "100Mbit", Effect: types.TaintEffectNoSchedule}},
|
||||||
types.Resources{Requests: map[string]string{"memory": "128Mi", "cpu": "1000m"}, Limits: map[string]string{"memory": "256Mi", "cpu": "2"}},
|
types.Resources{Requests: map[string]string{"memory": "128Mi", "cpu": "1000m"}, Limits: map[string]string{"memory": "256Mi", "cpu": "2"}},
|
||||||
|
Reference in New Issue
Block a user