From ef5e661a7b4b9677efee9f0791869308ac39cbba Mon Sep 17 00:00:00 2001 From: Dmitriy Ermakov Date: Fri, 22 Nov 2019 21:53:04 +0300 Subject: [PATCH 01/10] Mount Secrets and ConfigMaps to Kaniko Pod #3226 Signed-off-by: Dmitriy Ermakov --- pkg/skaffold/build/cluster/sources/sources.go | 67 ++++++++++++++++++- pkg/skaffold/schema/latest/config.go | 54 +++++++++++++++ pkg/skaffold/schema/v1/upgrade.go | 1 + 3 files changed, 119 insertions(+), 3 deletions(-) diff --git a/pkg/skaffold/build/cluster/sources/sources.go b/pkg/skaffold/build/cluster/sources/sources.go index e0d15692624..68652bb676a 100644 --- a/pkg/skaffold/build/cluster/sources/sources.go +++ b/pkg/skaffold/build/cluster/sources/sources.go @@ -91,7 +91,7 @@ func podTemplate(clusterDetails *latest.ClusterDetails, artifact *latest.KanikoA // Add secret for pull secret if clusterDetails.PullSecretName != "" { - addSecretVolume(pod, constants.DefaultKanikoSecretName, clusterDetails.PullSecretMountPath, clusterDetails.PullSecretName) + addSecretVolume(pod, constants.DefaultKanikoSecretName, clusterDetails.PullSecretMountPath, clusterDetails.PullSecretName, nil) } // Add host path volume for cache @@ -104,11 +104,36 @@ func podTemplate(clusterDetails *latest.ClusterDetails, artifact *latest.KanikoA } // Add secret for docker config if specified - addSecretVolume(pod, constants.DefaultKanikoDockerConfigSecretName, constants.DefaultKanikoDockerConfigPath, clusterDetails.DockerConfig.SecretName) + addSecretVolume(pod, constants.DefaultKanikoDockerConfigSecretName, constants.DefaultKanikoDockerConfigPath, clusterDetails.DockerConfig.SecretName, nil) + + // Add user-configured ConfigMaps and Secrets + for _, vol := range clusterDetails.Volumes { + if vol.ConfigMap != nil { + addConfigMapVolume(pod, vol.ConfigMap.Name, vol.ConfigMap.MountPath, vol.ConfigMap.VolumeName, vol.ConfigMap.Items) + } + + if vol.Secret != nil { + addSecretVolume(pod, vol.Secret.Name, vol.Secret.MountPath, vol.Secret.VolumeName, vol.Secret.Items) + } + } + return pod } -func addSecretVolume(pod *v1.Pod, name, mountPath, secretName string) { +func addSecretVolume(pod *v1.Pod, name, mountPath, secretName string, items []latest.KeyToPath) { + // Create Secret items + var kp []v1.KeyToPath + if items != nil { + kp = make([]v1.KeyToPath, len(items)) + + for i, v := range items { + kp[i] = v1.KeyToPath{ + Key: v.Key, + Path: v.Path, + } + } + } + vm := v1.VolumeMount{ Name: name, MountPath: mountPath, @@ -118,9 +143,45 @@ func addSecretVolume(pod *v1.Pod, name, mountPath, secretName string) { VolumeSource: v1.VolumeSource{ Secret: &v1.SecretVolumeSource{ SecretName: secretName, + Items: kp, }, }, } + + pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, vm) + pod.Spec.Volumes = append(pod.Spec.Volumes, v) +} + +func addConfigMapVolume(pod *v1.Pod, name, mountPath, configMapName string, items []latest.KeyToPath) { + // Create ConfigMap items + var kp []v1.KeyToPath + if items != nil { + kp = make([]v1.KeyToPath, len(items)) + + for i, v := range items { + kp[i] = v1.KeyToPath{ + Key: v.Key, + Path: v.Path, + } + } + } + + vm := v1.VolumeMount{ + Name: name, + MountPath: mountPath, + } + v := v1.Volume{ + Name: name, + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: configMapName, + }, + Items: kp, + }, + }, + } + pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, vm) pod.Spec.Volumes = append(pod.Spec.Volumes, v) } diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 7f772471254..75c904f2384 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -310,6 +310,60 @@ type ClusterDetails struct { // Concurrency is how many artifacts can be built concurrently. 0 means "no-limit" // Defaults to 0. Concurrency int `yaml:"concurrency,omitempty"` + + // Volumes define container mounts for ConfigMap and Secret resources + Volumes []VolumeMount `yaml:"volumes,omitempty"` +} + +// VolumeMount represents a volume to mount to the kaniko container. +// Only one of its members may be specified. +type VolumeMount struct { + // ConfigMap specifies a ConfigMap mount into the kaniko pod container + ConfigMap *ConfigMapMount `yaml:"configMap,omitempty" yamltags:"oneOf=volumeType"` + + // Secret specifies a Secret mount into the kaniko pod container + Secret *SecretMount `yaml:"secret,omitempty" yamltags:"oneOf=volumeType"` +} + +// ConfigMapMount describes one ConfigMap mount to the kaniko container filesystem. +type ConfigMapMount struct { + // Name is the Kubernetes ConfigMap name + Name string `yaml:"name" yamltags:"required"` + + // Items if specified then only defined keys of the ConfigMap will be projected to the pod filesystem using relative paths as + // described in [volumes configMap](https://kubernetes.io/docs/concepts/storage/volumes/#configmap) + Items []KeyToPath `yaml:"items,omitempty"` + + // Defines the path to mount the ConfigMap + MountPath string `yaml:"mountPath" yamltags:"required"` + + // VolumeName defines Kubernetes pod.spec.volumes[].name for the Pod + VolumeName string `yaml:"volumeName" yamltags:"required"` +} + +// SecretMount describes one Secret mount to a kaniko container filesystem. +type SecretMount struct { + // Name is the Kubernetes Secret name + Name string `yaml:"name" yamltags:"required"` + + // Items if specified then only defined keys of the Secret will be projected to the pod filesystem using relative paths as + // described in [volumes secret](https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets) + Items []KeyToPath `yaml:"items,omitempty"` + + // Defines the path to mount the Secret + MountPath string `yaml:"mountPath" yamltags:"required"` + + // VolumeName defines Kubernetes pod.spec.volumes[].name for the Pod + VolumeName string `yaml:"volumeName" yamltags:"required"` +} + +// KeyToPath describes mapping from ConfigMap or Secret resource key to a path within a volume. +type KeyToPath struct { + // Key is the key to get from the Resource. + Key string `yaml:"key" yamltags:"required"` + + // Path is the relative path to mount Key to. + Path string `yaml:"path" yamltags:"required"` } // DockerConfig contains information about the docker `config.json` to mount. diff --git a/pkg/skaffold/schema/v1/upgrade.go b/pkg/skaffold/schema/v1/upgrade.go index 9fb400dd069..b77acdc507b 100755 --- a/pkg/skaffold/schema/v1/upgrade.go +++ b/pkg/skaffold/schema/v1/upgrade.go @@ -25,6 +25,7 @@ import ( // Upgrade upgrades a configuration to the next version. // Config changes from v1 to v2alpha1 // 1. Additions: +// Add Volumes, VolumeMount, ConfigMapMount and SecretMount to ClusterDetails. // 2. Removals: // 3. No updates func (c *SkaffoldConfig) Upgrade() (util.VersionedConfig, error) { From a3254f72bcf6ead3a9c3904514940fca028291e6 Mon Sep 17 00:00:00 2001 From: Dmitriy Ermakov Date: Fri, 22 Nov 2019 23:29:33 +0300 Subject: [PATCH 02/10] Fixed incorrect call order #3226 Signed-off-by: Dmitriy Ermakov --- pkg/skaffold/build/cluster/sources/sources.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/skaffold/build/cluster/sources/sources.go b/pkg/skaffold/build/cluster/sources/sources.go index 68652bb676a..87c9849e3d4 100644 --- a/pkg/skaffold/build/cluster/sources/sources.go +++ b/pkg/skaffold/build/cluster/sources/sources.go @@ -99,24 +99,24 @@ func podTemplate(clusterDetails *latest.ClusterDetails, artifact *latest.KanikoA addHostPathVolume(pod, constants.DefaultKanikoCacheDirName, constants.DefaultKanikoCacheDirMountPath, artifact.Cache.HostPath) } - if clusterDetails.DockerConfig == nil { - return pod - } - - // Add secret for docker config if specified - addSecretVolume(pod, constants.DefaultKanikoDockerConfigSecretName, constants.DefaultKanikoDockerConfigPath, clusterDetails.DockerConfig.SecretName, nil) - // Add user-configured ConfigMaps and Secrets for _, vol := range clusterDetails.Volumes { if vol.ConfigMap != nil { - addConfigMapVolume(pod, vol.ConfigMap.Name, vol.ConfigMap.MountPath, vol.ConfigMap.VolumeName, vol.ConfigMap.Items) + addConfigMapVolume(pod, vol.ConfigMap.VolumeName, vol.ConfigMap.MountPath, vol.ConfigMap.Name, vol.ConfigMap.Items) } if vol.Secret != nil { - addSecretVolume(pod, vol.Secret.Name, vol.Secret.MountPath, vol.Secret.VolumeName, vol.Secret.Items) + addSecretVolume(pod, vol.Secret.VolumeName, vol.Secret.MountPath, vol.Secret.Name, vol.Secret.Items) } } + if clusterDetails.DockerConfig == nil { + return pod + } + + // Add secret for docker config if specified + addSecretVolume(pod, constants.DefaultKanikoDockerConfigSecretName, constants.DefaultKanikoDockerConfigPath, clusterDetails.DockerConfig.SecretName, nil) + return pod } From a83fab03d904d938d84d5adb0a78858af1124862 Mon Sep 17 00:00:00 2001 From: Dmitriy Ermakov Date: Fri, 22 Nov 2019 23:30:00 +0300 Subject: [PATCH 03/10] Added unit tests for new code #3226 Signed-off-by: Dmitriy Ermakov --- .../build/cluster/sources/sources_test.go | 296 ++++++++++++++++++ 1 file changed, 296 insertions(+) diff --git a/pkg/skaffold/build/cluster/sources/sources_test.go b/pkg/skaffold/build/cluster/sources/sources_test.go index 05dfac615f3..55362c8894f 100644 --- a/pkg/skaffold/build/cluster/sources/sources_test.go +++ b/pkg/skaffold/build/cluster/sources/sources_test.go @@ -335,3 +335,299 @@ func TestResourceRequirements(t *testing.T) { }) } } + +func TestSecretVolume(t *testing.T) { + tests := []struct { + description string + initial *latest.ClusterDetails + artifact *latest.KanikoArtifact + expected *v1.Pod + }{ + { + description: "secret volume", + initial: &latest.ClusterDetails{ + Volumes: []latest.VolumeMount{ + { + Secret: &latest.SecretMount{ + Name: "kubernetes-secret", + MountPath: "/mount-dir", + VolumeName: "kubernetes-secret-volume", + }, + }, + }, + }, + artifact: &latest.KanikoArtifact{ + Image: "kaniko-latest", + }, + expected: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "kaniko-", + Labels: map[string]string{"skaffold-kaniko": "skaffold-kaniko"}, + }, + Spec: v1.PodSpec{ + RestartPolicy: "Never", + Containers: []v1.Container{ + { + Name: "kaniko", + Image: "kaniko-latest", + Env: []v1.EnvVar{{ + Name: "GOOGLE_APPLICATION_CREDENTIALS", + Value: "/secret/kaniko-secret", + }, { + Name: "UPSTREAM_CLIENT_TYPE", + Value: "UpstreamClient(skaffold-test)", + }}, + ImagePullPolicy: v1.PullPolicy("IfNotPresent"), + VolumeMounts: []v1.VolumeMount{ + { + Name: "kubernetes-secret-volume", + MountPath: "/mount-dir", + }, + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "kubernetes-secret-volume", + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: "kubernetes-secret", + }, + }, + }, + }, + }, + }, + }, + { + description: "secret volume with items", + initial: &latest.ClusterDetails{ + Volumes: []latest.VolumeMount{ + { + Secret: &latest.SecretMount{ + Name: "kubernetes-secret", + MountPath: "/mount-dir", + VolumeName: "kubernetes-secret-volume", + Items: []latest.KeyToPath{ + latest.KeyToPath{ + Key: "secret-file", + Path: "secret/subpath", + }, + }, + }, + }, + }, + }, + artifact: &latest.KanikoArtifact{ + Image: "kaniko-latest", + }, + expected: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "kaniko-", + Labels: map[string]string{"skaffold-kaniko": "skaffold-kaniko"}, + }, + Spec: v1.PodSpec{ + RestartPolicy: "Never", + Containers: []v1.Container{ + { + Name: "kaniko", + Image: "kaniko-latest", + Env: []v1.EnvVar{{ + Name: "GOOGLE_APPLICATION_CREDENTIALS", + Value: "/secret/kaniko-secret", + }, { + Name: "UPSTREAM_CLIENT_TYPE", + Value: "UpstreamClient(skaffold-test)", + }}, + ImagePullPolicy: v1.PullPolicy("IfNotPresent"), + VolumeMounts: []v1.VolumeMount{ + { + Name: "kubernetes-secret-volume", + MountPath: "/mount-dir", + }, + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "kubernetes-secret-volume", + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: "kubernetes-secret", + Items: []v1.KeyToPath{ + v1.KeyToPath{ + Key: "secret-file", + Path: "secret/subpath", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + opt := cmp.Comparer(func(x, y resource.Quantity) bool { + return x.String() == y.String() + }) + + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + actual := podTemplate(test.initial, test.artifact, nil, "test") + + t.CheckDeepEqual(test.expected, actual, opt) + }) + } +} + +func TestConfigMapVolume(t *testing.T) { + tests := []struct { + description string + initial *latest.ClusterDetails + artifact *latest.KanikoArtifact + expected *v1.Pod + }{ + { + description: "config-map volume", + initial: &latest.ClusterDetails{ + Volumes: []latest.VolumeMount{ + { + ConfigMap: &latest.ConfigMapMount{ + Name: "kubernetes-config-map", + MountPath: "/mount-dir", + VolumeName: "kubernetes-config-map-volume", + }, + }, + }, + }, + artifact: &latest.KanikoArtifact{ + Image: "kaniko-latest", + }, + expected: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "kaniko-", + Labels: map[string]string{"skaffold-kaniko": "skaffold-kaniko"}, + }, + Spec: v1.PodSpec{ + RestartPolicy: "Never", + Containers: []v1.Container{ + { + Name: "kaniko", + Image: "kaniko-latest", + Env: []v1.EnvVar{{ + Name: "GOOGLE_APPLICATION_CREDENTIALS", + Value: "/secret/kaniko-secret", + }, { + Name: "UPSTREAM_CLIENT_TYPE", + Value: "UpstreamClient(skaffold-test)", + }}, + ImagePullPolicy: v1.PullPolicy("IfNotPresent"), + VolumeMounts: []v1.VolumeMount{ + { + Name: "kubernetes-config-map-volume", + MountPath: "/mount-dir", + }, + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "kubernetes-config-map-volume", + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "kubernetes-config-map", + }, + }, + }, + }, + }, + }, + }, + }, + { + description: "config-map volume with items", + initial: &latest.ClusterDetails{ + Volumes: []latest.VolumeMount{ + { + ConfigMap: &latest.ConfigMapMount{ + Name: "kubernetes-config-map", + MountPath: "/mount-dir", + VolumeName: "kubernetes-config-map-volume", + Items: []latest.KeyToPath{ + latest.KeyToPath{ + Key: "config-map-file", + Path: "config-map/subpath", + }, + }, + }, + }, + }, + }, + artifact: &latest.KanikoArtifact{ + Image: "kaniko-latest", + }, + expected: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "kaniko-", + Labels: map[string]string{"skaffold-kaniko": "skaffold-kaniko"}, + }, + Spec: v1.PodSpec{ + RestartPolicy: "Never", + Containers: []v1.Container{ + { + Name: "kaniko", + Image: "kaniko-latest", + Env: []v1.EnvVar{{ + Name: "GOOGLE_APPLICATION_CREDENTIALS", + Value: "/secret/kaniko-secret", + }, { + Name: "UPSTREAM_CLIENT_TYPE", + Value: "UpstreamClient(skaffold-test)", + }}, + ImagePullPolicy: v1.PullPolicy("IfNotPresent"), + VolumeMounts: []v1.VolumeMount{ + { + Name: "kubernetes-config-map-volume", + MountPath: "/mount-dir", + }, + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "kubernetes-config-map-volume", + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "kubernetes-config-map", + }, + Items: []v1.KeyToPath{ + v1.KeyToPath{ + Key: "config-map-file", + Path: "config-map/subpath", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + opt := cmp.Comparer(func(x, y resource.Quantity) bool { + return x.String() == y.String() + }) + + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + actual := podTemplate(test.initial, test.artifact, nil, "test") + + t.CheckDeepEqual(test.expected, actual, opt) + }) + } +} From e6c4e4ed1f388067f5883e1d756bae0eefa2dd10 Mon Sep 17 00:00:00 2001 From: Dmitriy Ermakov Date: Sat, 23 Nov 2019 08:18:06 +0300 Subject: [PATCH 04/10] fixes linter errors Signed-off-by: Dmitriy Ermakov --- pkg/skaffold/build/cluster/sources/sources_test.go | 8 ++++---- pkg/skaffold/schema/latest/config.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/skaffold/build/cluster/sources/sources_test.go b/pkg/skaffold/build/cluster/sources/sources_test.go index 55362c8894f..13995a21e34 100644 --- a/pkg/skaffold/build/cluster/sources/sources_test.go +++ b/pkg/skaffold/build/cluster/sources/sources_test.go @@ -409,7 +409,7 @@ func TestSecretVolume(t *testing.T) { MountPath: "/mount-dir", VolumeName: "kubernetes-secret-volume", Items: []latest.KeyToPath{ - latest.KeyToPath{ + { Key: "secret-file", Path: "secret/subpath", }, @@ -455,7 +455,7 @@ func TestSecretVolume(t *testing.T) { Secret: &v1.SecretVolumeSource{ SecretName: "kubernetes-secret", Items: []v1.KeyToPath{ - v1.KeyToPath{ + { Key: "secret-file", Path: "secret/subpath", }, @@ -557,7 +557,7 @@ func TestConfigMapVolume(t *testing.T) { MountPath: "/mount-dir", VolumeName: "kubernetes-config-map-volume", Items: []latest.KeyToPath{ - latest.KeyToPath{ + { Key: "config-map-file", Path: "config-map/subpath", }, @@ -605,7 +605,7 @@ func TestConfigMapVolume(t *testing.T) { Name: "kubernetes-config-map", }, Items: []v1.KeyToPath{ - v1.KeyToPath{ + { Key: "config-map-file", Path: "config-map/subpath", }, diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 75c904f2384..7a220c1c53d 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -311,7 +311,7 @@ type ClusterDetails struct { // Defaults to 0. Concurrency int `yaml:"concurrency,omitempty"` - // Volumes define container mounts for ConfigMap and Secret resources + // Volumes define container mounts for ConfigMap and Secret resources. Volumes []VolumeMount `yaml:"volumes,omitempty"` } From 56320e49e26c1d36f3eef6a0c54d4fcb5d6f51c7 Mon Sep 17 00:00:00 2001 From: Dmitriy Ermakov Date: Sat, 23 Nov 2019 09:07:18 +0300 Subject: [PATCH 05/10] fixes linter errors Signed-off-by: Dmitriy Ermakov --- pkg/skaffold/schema/latest/config.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 7a220c1c53d..9e9badcd3c3 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -318,42 +318,42 @@ type ClusterDetails struct { // VolumeMount represents a volume to mount to the kaniko container. // Only one of its members may be specified. type VolumeMount struct { - // ConfigMap specifies a ConfigMap mount into the kaniko pod container + // ConfigMap specifies a ConfigMap mount into the kaniko pod container. ConfigMap *ConfigMapMount `yaml:"configMap,omitempty" yamltags:"oneOf=volumeType"` - // Secret specifies a Secret mount into the kaniko pod container + // Secret specifies a Secret mount into the kaniko pod container. Secret *SecretMount `yaml:"secret,omitempty" yamltags:"oneOf=volumeType"` } // ConfigMapMount describes one ConfigMap mount to the kaniko container filesystem. type ConfigMapMount struct { - // Name is the Kubernetes ConfigMap name + // Name is the Kubernetes ConfigMap name. Name string `yaml:"name" yamltags:"required"` // Items if specified then only defined keys of the ConfigMap will be projected to the pod filesystem using relative paths as - // described in [volumes configMap](https://kubernetes.io/docs/concepts/storage/volumes/#configmap) + // described in [volumes configMap](https://kubernetes.io/docs/concepts/storage/volumes/#configmap). Items []KeyToPath `yaml:"items,omitempty"` - // Defines the path to mount the ConfigMap + // MountPath defines the path to mount the ConfigMap. MountPath string `yaml:"mountPath" yamltags:"required"` - // VolumeName defines Kubernetes pod.spec.volumes[].name for the Pod + // VolumeName defines Kubernetes pod.spec.volumes[].name for the Pod. VolumeName string `yaml:"volumeName" yamltags:"required"` } // SecretMount describes one Secret mount to a kaniko container filesystem. type SecretMount struct { - // Name is the Kubernetes Secret name + // Name is the Kubernetes Secret name. Name string `yaml:"name" yamltags:"required"` // Items if specified then only defined keys of the Secret will be projected to the pod filesystem using relative paths as - // described in [volumes secret](https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets) + // described in [volumes secret](https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets). Items []KeyToPath `yaml:"items,omitempty"` - // Defines the path to mount the Secret + // MountPath defines the path to mount the Secret. MountPath string `yaml:"mountPath" yamltags:"required"` - // VolumeName defines Kubernetes pod.spec.volumes[].name for the Pod + // VolumeName defines Kubernetes pod.spec.volumes[].name for the Pod. VolumeName string `yaml:"volumeName" yamltags:"required"` } From 4414138acbf4624598d956411e326c645ac67155 Mon Sep 17 00:00:00 2001 From: Dmitriy Ermakov Date: Sat, 23 Nov 2019 09:07:45 +0300 Subject: [PATCH 06/10] updates JSON schema with new resources Signed-off-by: Dmitriy Ermakov --- docs/content/en/schemas/v2alpha1.json | 139 +++++++++++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) diff --git a/docs/content/en/schemas/v2alpha1.json b/docs/content/en/schemas/v2alpha1.json index d514c464a49..0db0d00907b 100755 --- a/docs/content/en/schemas/v2alpha1.json +++ b/docs/content/en/schemas/v2alpha1.json @@ -608,6 +608,14 @@ "type": "string", "description": "amount of time (in seconds) that this build is allowed to run. Defaults to 20 minutes (`20m`).", "x-intellij-html-description": "amount of time (in seconds) that this build is allowed to run. Defaults to 20 minutes (20m)." + }, + "volumes": { + "items": { + "$ref": "#/definitions/VolumeMount" + }, + "type": "array", + "description": "define container mounts for ConfigMap and Secret resources.", + "x-intellij-html-description": "define container mounts for ConfigMap and Secret resources." } }, "preferredOrder": [ @@ -620,12 +628,54 @@ "timeout", "dockerConfig", "resources", - "concurrency" + "concurrency", + "volumes" ], "additionalProperties": false, "description": "*beta* describes how to do an on-cluster build.", "x-intellij-html-description": "beta describes how to do an on-cluster build." }, + "ConfigMapMount": { + "required": [ + "name", + "mountPath", + "volumeName" + ], + "properties": { + "items": { + "items": { + "$ref": "#/definitions/KeyToPath" + }, + "type": "array", + "description": "if specified then only defined keys of the ConfigMap will be projected to the pod filesystem using relative paths as described in [volumes configMap](https://kubernetes.io/docs/concepts/storage/volumes/#configmap).", + "x-intellij-html-description": "if specified then only defined keys of the ConfigMap will be projected to the pod filesystem using relative paths as described in volumes configMap." + }, + "mountPath": { + "type": "string", + "description": "defines the path to mount the ConfigMap.", + "x-intellij-html-description": "defines the path to mount the ConfigMap." + }, + "name": { + "type": "string", + "description": "Kubernetes ConfigMap name.", + "x-intellij-html-description": "Kubernetes ConfigMap name." + }, + "volumeName": { + "type": "string", + "description": "defines Kubernetes pod.spec.volumes[].name for the Pod.", + "x-intellij-html-description": "defines Kubernetes pod.spec.volumes[].name for the Pod." + } + }, + "preferredOrder": [ + "name", + "items", + "mountPath", + "volumeName" + ], + "additionalProperties": false, + "description": "describes one ConfigMap mount to the kaniko container filesystem.", + "x-intellij-html-description": "describes one ConfigMap mount to the kaniko container filesystem." + }, "CustomArtifact": { "properties": { "buildCommand": { @@ -1536,6 +1586,31 @@ "description": "configures Kaniko caching. If a cache is specified, Kaniko will use a remote cache which will speed up builds.", "x-intellij-html-description": "configures Kaniko caching. If a cache is specified, Kaniko will use a remote cache which will speed up builds." }, + "KeyToPath": { + "required": [ + "key", + "path" + ], + "properties": { + "key": { + "type": "string", + "description": "key to get from the Resource.", + "x-intellij-html-description": "key to get from the Resource." + }, + "path": { + "type": "string", + "description": "relative path to mount Key to.", + "x-intellij-html-description": "relative path to mount Key to." + } + }, + "preferredOrder": [ + "key", + "path" + ], + "additionalProperties": false, + "description": "describes mapping from ConfigMap or Secret resource key to a path within a volume.", + "x-intellij-html-description": "describes mapping from ConfigMap or Secret resource key to a path within a volume." + }, "KubectlDeploy": { "properties": { "flags": { @@ -1886,6 +1961,47 @@ "description": "describes the Kubernetes resource types used for port forwarding.", "x-intellij-html-description": "describes the Kubernetes resource types used for port forwarding." }, + "SecretMount": { + "required": [ + "name", + "mountPath", + "volumeName" + ], + "properties": { + "items": { + "items": { + "$ref": "#/definitions/KeyToPath" + }, + "type": "array", + "description": "if specified then only defined keys of the Secret will be projected to the pod filesystem using relative paths as described in [volumes secret](https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets).", + "x-intellij-html-description": "if specified then only defined keys of the Secret will be projected to the pod filesystem using relative paths as described in volumes secret." + }, + "mountPath": { + "type": "string", + "description": "defines the path to mount the Secret.", + "x-intellij-html-description": "defines the path to mount the Secret." + }, + "name": { + "type": "string", + "description": "Kubernetes Secret name.", + "x-intellij-html-description": "Kubernetes Secret name." + }, + "volumeName": { + "type": "string", + "description": "defines Kubernetes pod.spec.volumes[].name for the Pod.", + "x-intellij-html-description": "defines Kubernetes pod.spec.volumes[].name for the Pod." + } + }, + "preferredOrder": [ + "name", + "items", + "mountPath", + "volumeName" + ], + "additionalProperties": false, + "description": "describes one Secret mount to a kaniko container filesystem.", + "x-intellij-html-description": "describes one Secret mount to a kaniko container filesystem." + }, "ShaTagger": { "description": "*beta* tags images with their sha256 digest.", "x-intellij-html-description": "beta tags images with their sha256 digest." @@ -2095,6 +2211,27 @@ "additionalProperties": false, "description": "a list of structure tests to run on images that Skaffold builds.", "x-intellij-html-description": "a list of structure tests to run on images that Skaffold builds." + }, + "VolumeMount": { + "properties": { + "configMap": { + "$ref": "#/definitions/ConfigMapMount", + "description": "specifies a ConfigMap mount into the kaniko pod container.", + "x-intellij-html-description": "specifies a ConfigMap mount into the kaniko pod container." + }, + "secret": { + "$ref": "#/definitions/SecretMount", + "description": "specifies a Secret mount into the kaniko pod container.", + "x-intellij-html-description": "specifies a Secret mount into the kaniko pod container." + } + }, + "preferredOrder": [ + "configMap", + "secret" + ], + "additionalProperties": false, + "description": "represents a volume to mount to the kaniko container. Only one of its members may be specified.", + "x-intellij-html-description": "represents a volume to mount to the kaniko container. Only one of its members may be specified." } } } From 1054d5e1e7f72bf32ae6b38505d03609e0e7bb26 Mon Sep 17 00:00:00 2001 From: Dmitriy Ermakov Date: Sat, 23 Nov 2019 10:16:56 +0300 Subject: [PATCH 07/10] Implements setting Volume Permissions Signed-off-by: Dmitriy Ermakov --- docs/content/en/schemas/v2alpha1.json | 24 +- pkg/skaffold/build/cluster/sources/sources.go | 27 +- .../build/cluster/sources/sources_test.go | 432 ++++++++++++++++++ pkg/skaffold/schema/latest/config.go | 12 + 4 files changed, 483 insertions(+), 12 deletions(-) diff --git a/docs/content/en/schemas/v2alpha1.json b/docs/content/en/schemas/v2alpha1.json index 0db0d00907b..7fcf64b2d86 100755 --- a/docs/content/en/schemas/v2alpha1.json +++ b/docs/content/en/schemas/v2alpha1.json @@ -642,6 +642,11 @@ "volumeName" ], "properties": { + "defaultMode": { + "type": "integer", + "description": "UNIX mode bits to set on mounted files. Must be a value between 0 and 0777. Defaults to 0644.", + "x-intellij-html-description": "UNIX mode bits to set on mounted files. Must be a value between 0 and 0777. Defaults to 0644." + }, "items": { "items": { "$ref": "#/definitions/KeyToPath" @@ -670,7 +675,8 @@ "name", "items", "mountPath", - "volumeName" + "volumeName", + "defaultMode" ], "additionalProperties": false, "description": "describes one ConfigMap mount to the kaniko container filesystem.", @@ -1597,6 +1603,11 @@ "description": "key to get from the Resource.", "x-intellij-html-description": "key to get from the Resource." }, + "mode": { + "type": "integer", + "description": "UNIX mode bits to set on mounted files. Must be a value between 0 and 0777. Defaults to 0644.", + "x-intellij-html-description": "UNIX mode bits to set on mounted files. Must be a value between 0 and 0777. Defaults to 0644." + }, "path": { "type": "string", "description": "relative path to mount Key to.", @@ -1605,7 +1616,8 @@ }, "preferredOrder": [ "key", - "path" + "path", + "mode" ], "additionalProperties": false, "description": "describes mapping from ConfigMap or Secret resource key to a path within a volume.", @@ -1968,6 +1980,11 @@ "volumeName" ], "properties": { + "defaultMode": { + "type": "integer", + "description": "UNIX mode bits to set on mounted files. Must be a value between 0 and 0777. Defaults to 0644.", + "x-intellij-html-description": "UNIX mode bits to set on mounted files. Must be a value between 0 and 0777. Defaults to 0644." + }, "items": { "items": { "$ref": "#/definitions/KeyToPath" @@ -1996,7 +2013,8 @@ "name", "items", "mountPath", - "volumeName" + "volumeName", + "defaultMode" ], "additionalProperties": false, "description": "describes one Secret mount to a kaniko container filesystem.", diff --git a/pkg/skaffold/build/cluster/sources/sources.go b/pkg/skaffold/build/cluster/sources/sources.go index 87c9849e3d4..a741c485b41 100644 --- a/pkg/skaffold/build/cluster/sources/sources.go +++ b/pkg/skaffold/build/cluster/sources/sources.go @@ -91,7 +91,7 @@ func podTemplate(clusterDetails *latest.ClusterDetails, artifact *latest.KanikoA // Add secret for pull secret if clusterDetails.PullSecretName != "" { - addSecretVolume(pod, constants.DefaultKanikoSecretName, clusterDetails.PullSecretMountPath, clusterDetails.PullSecretName, nil) + addSecretVolume(pod, constants.DefaultKanikoSecretName, clusterDetails.PullSecretMountPath, clusterDetails.PullSecretName, nil, nil) } // Add host path volume for cache @@ -102,11 +102,14 @@ func podTemplate(clusterDetails *latest.ClusterDetails, artifact *latest.KanikoA // Add user-configured ConfigMaps and Secrets for _, vol := range clusterDetails.Volumes { if vol.ConfigMap != nil { - addConfigMapVolume(pod, vol.ConfigMap.VolumeName, vol.ConfigMap.MountPath, vol.ConfigMap.Name, vol.ConfigMap.Items) + addConfigMapVolume(pod, vol.ConfigMap.VolumeName, + vol.ConfigMap.MountPath, vol.ConfigMap.Name, + vol.ConfigMap.Items, vol.ConfigMap.DefaultMode) } if vol.Secret != nil { - addSecretVolume(pod, vol.Secret.VolumeName, vol.Secret.MountPath, vol.Secret.Name, vol.Secret.Items) + addSecretVolume(pod, vol.Secret.VolumeName, vol.Secret.MountPath, + vol.Secret.Name, vol.Secret.Items, vol.Secret.DefaultMode) } } @@ -115,12 +118,12 @@ func podTemplate(clusterDetails *latest.ClusterDetails, artifact *latest.KanikoA } // Add secret for docker config if specified - addSecretVolume(pod, constants.DefaultKanikoDockerConfigSecretName, constants.DefaultKanikoDockerConfigPath, clusterDetails.DockerConfig.SecretName, nil) + addSecretVolume(pod, constants.DefaultKanikoDockerConfigSecretName, constants.DefaultKanikoDockerConfigPath, clusterDetails.DockerConfig.SecretName, nil, nil) return pod } -func addSecretVolume(pod *v1.Pod, name, mountPath, secretName string, items []latest.KeyToPath) { +func addSecretVolume(pod *v1.Pod, name, mountPath, secretName string, items []latest.KeyToPath, defaultMode *int32) { // Create Secret items var kp []v1.KeyToPath if items != nil { @@ -130,6 +133,7 @@ func addSecretVolume(pod *v1.Pod, name, mountPath, secretName string, items []la kp[i] = v1.KeyToPath{ Key: v.Key, Path: v.Path, + Mode: v.Mode, } } } @@ -142,8 +146,9 @@ func addSecretVolume(pod *v1.Pod, name, mountPath, secretName string, items []la Name: name, VolumeSource: v1.VolumeSource{ Secret: &v1.SecretVolumeSource{ - SecretName: secretName, - Items: kp, + SecretName: secretName, + Items: kp, + DefaultMode: defaultMode, }, }, } @@ -152,7 +157,8 @@ func addSecretVolume(pod *v1.Pod, name, mountPath, secretName string, items []la pod.Spec.Volumes = append(pod.Spec.Volumes, v) } -func addConfigMapVolume(pod *v1.Pod, name, mountPath, configMapName string, items []latest.KeyToPath) { +func addConfigMapVolume(pod *v1.Pod, name, mountPath, configMapName string, + items []latest.KeyToPath, defaultMode *int32) { // Create ConfigMap items var kp []v1.KeyToPath if items != nil { @@ -162,6 +168,7 @@ func addConfigMapVolume(pod *v1.Pod, name, mountPath, configMapName string, item kp[i] = v1.KeyToPath{ Key: v.Key, Path: v.Path, + Mode: v.Mode, } } } @@ -177,7 +184,8 @@ func addConfigMapVolume(pod *v1.Pod, name, mountPath, configMapName string, item LocalObjectReference: v1.LocalObjectReference{ Name: configMapName, }, - Items: kp, + Items: kp, + DefaultMode: defaultMode, }, }, } @@ -199,6 +207,7 @@ func addHostPathVolume(pod *v1.Pod, name, mountPath, path string) { }, }, } + pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, vm) pod.Spec.Volumes = append(pod.Spec.Volumes, v) } diff --git a/pkg/skaffold/build/cluster/sources/sources_test.go b/pkg/skaffold/build/cluster/sources/sources_test.go index 13995a21e34..eb7d2110ec5 100644 --- a/pkg/skaffold/build/cluster/sources/sources_test.go +++ b/pkg/skaffold/build/cluster/sources/sources_test.go @@ -337,6 +337,11 @@ func TestResourceRequirements(t *testing.T) { } func TestSecretVolume(t *testing.T) { + var ( + defaultFilePermissions int32 = 0x0400 + itemsFilePermissions int32 = 0x0500 + ) + tests := []struct { description string initial *latest.ClusterDetails @@ -399,6 +404,64 @@ func TestSecretVolume(t *testing.T) { }, }, }, + { + description: "secret volume with permissions", + initial: &latest.ClusterDetails{ + Volumes: []latest.VolumeMount{ + { + Secret: &latest.SecretMount{ + Name: "kubernetes-secret", + MountPath: "/mount-dir", + VolumeName: "kubernetes-secret-volume", + DefaultMode: &defaultFilePermissions, + }, + }, + }, + }, + artifact: &latest.KanikoArtifact{ + Image: "kaniko-latest", + }, + expected: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "kaniko-", + Labels: map[string]string{"skaffold-kaniko": "skaffold-kaniko"}, + }, + Spec: v1.PodSpec{ + RestartPolicy: "Never", + Containers: []v1.Container{ + { + Name: "kaniko", + Image: "kaniko-latest", + Env: []v1.EnvVar{{ + Name: "GOOGLE_APPLICATION_CREDENTIALS", + Value: "/secret/kaniko-secret", + }, { + Name: "UPSTREAM_CLIENT_TYPE", + Value: "UpstreamClient(skaffold-test)", + }}, + ImagePullPolicy: v1.PullPolicy("IfNotPresent"), + VolumeMounts: []v1.VolumeMount{ + { + Name: "kubernetes-secret-volume", + MountPath: "/mount-dir", + }, + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "kubernetes-secret-volume", + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: "kubernetes-secret", + DefaultMode: &defaultFilePermissions, + }, + }, + }, + }, + }, + }, + }, { description: "secret volume with items", initial: &latest.ClusterDetails{ @@ -467,6 +530,156 @@ func TestSecretVolume(t *testing.T) { }, }, }, + { + description: "secret volume with items and permissions", + initial: &latest.ClusterDetails{ + Volumes: []latest.VolumeMount{ + { + Secret: &latest.SecretMount{ + Name: "kubernetes-secret", + MountPath: "/mount-dir", + VolumeName: "kubernetes-secret-volume", + Items: []latest.KeyToPath{ + { + Key: "secret-file", + Path: "secret/subpath", + Mode: &itemsFilePermissions, + }, + }, + }, + }, + }, + }, + artifact: &latest.KanikoArtifact{ + Image: "kaniko-latest", + }, + expected: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "kaniko-", + Labels: map[string]string{"skaffold-kaniko": "skaffold-kaniko"}, + }, + Spec: v1.PodSpec{ + RestartPolicy: "Never", + Containers: []v1.Container{ + { + Name: "kaniko", + Image: "kaniko-latest", + Env: []v1.EnvVar{{ + Name: "GOOGLE_APPLICATION_CREDENTIALS", + Value: "/secret/kaniko-secret", + }, { + Name: "UPSTREAM_CLIENT_TYPE", + Value: "UpstreamClient(skaffold-test)", + }}, + ImagePullPolicy: v1.PullPolicy("IfNotPresent"), + VolumeMounts: []v1.VolumeMount{ + { + Name: "kubernetes-secret-volume", + MountPath: "/mount-dir", + }, + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "kubernetes-secret-volume", + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: "kubernetes-secret", + Items: []v1.KeyToPath{ + { + Key: "secret-file", + Path: "secret/subpath", + Mode: &itemsFilePermissions, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + description: "secret volume with default and per-item permissions", + initial: &latest.ClusterDetails{ + Volumes: []latest.VolumeMount{ + { + Secret: &latest.SecretMount{ + Name: "kubernetes-secret", + MountPath: "/mount-dir", + VolumeName: "kubernetes-secret-volume", + DefaultMode: &defaultFilePermissions, + Items: []latest.KeyToPath{ + { + Key: "secret-file-default-permissions", + Path: "secret/subpath", + }, + { + Key: "secret-file-custom-permissions", + Path: "secret/subpath", + Mode: &itemsFilePermissions, + }, + }, + }, + }, + }, + }, + artifact: &latest.KanikoArtifact{ + Image: "kaniko-latest", + }, + expected: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "kaniko-", + Labels: map[string]string{"skaffold-kaniko": "skaffold-kaniko"}, + }, + Spec: v1.PodSpec{ + RestartPolicy: "Never", + Containers: []v1.Container{ + { + Name: "kaniko", + Image: "kaniko-latest", + Env: []v1.EnvVar{{ + Name: "GOOGLE_APPLICATION_CREDENTIALS", + Value: "/secret/kaniko-secret", + }, { + Name: "UPSTREAM_CLIENT_TYPE", + Value: "UpstreamClient(skaffold-test)", + }}, + ImagePullPolicy: v1.PullPolicy("IfNotPresent"), + VolumeMounts: []v1.VolumeMount{ + { + Name: "kubernetes-secret-volume", + MountPath: "/mount-dir", + }, + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "kubernetes-secret-volume", + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: "kubernetes-secret", + DefaultMode: &defaultFilePermissions, + Items: []v1.KeyToPath{ + { + Key: "secret-file-default-permissions", + Path: "secret/subpath", + }, + { + Key: "secret-file-custom-permissions", + Path: "secret/subpath", + Mode: &itemsFilePermissions, + }, + }, + }, + }, + }, + }, + }, + }, + }, } opt := cmp.Comparer(func(x, y resource.Quantity) bool { @@ -483,6 +696,11 @@ func TestSecretVolume(t *testing.T) { } func TestConfigMapVolume(t *testing.T) { + var ( + defaultFilePermissions int32 = 0x0400 + itemsFilePermissions int32 = 0x0500 + ) + tests := []struct { description string initial *latest.ClusterDetails @@ -547,6 +765,66 @@ func TestConfigMapVolume(t *testing.T) { }, }, }, + { + description: "config-map volume with permissions", + initial: &latest.ClusterDetails{ + Volumes: []latest.VolumeMount{ + { + ConfigMap: &latest.ConfigMapMount{ + Name: "kubernetes-config-map", + MountPath: "/mount-dir", + VolumeName: "kubernetes-config-map-volume", + DefaultMode: &defaultFilePermissions, + }, + }, + }, + }, + artifact: &latest.KanikoArtifact{ + Image: "kaniko-latest", + }, + expected: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "kaniko-", + Labels: map[string]string{"skaffold-kaniko": "skaffold-kaniko"}, + }, + Spec: v1.PodSpec{ + RestartPolicy: "Never", + Containers: []v1.Container{ + { + Name: "kaniko", + Image: "kaniko-latest", + Env: []v1.EnvVar{{ + Name: "GOOGLE_APPLICATION_CREDENTIALS", + Value: "/secret/kaniko-secret", + }, { + Name: "UPSTREAM_CLIENT_TYPE", + Value: "UpstreamClient(skaffold-test)", + }}, + ImagePullPolicy: v1.PullPolicy("IfNotPresent"), + VolumeMounts: []v1.VolumeMount{ + { + Name: "kubernetes-config-map-volume", + MountPath: "/mount-dir", + }, + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "kubernetes-config-map-volume", + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "kubernetes-config-map", + }, + DefaultMode: &defaultFilePermissions, + }, + }, + }, + }, + }, + }, + }, { description: "config-map volume with items", initial: &latest.ClusterDetails{ @@ -617,6 +895,160 @@ func TestConfigMapVolume(t *testing.T) { }, }, }, + { + description: "config-map volume with items and permissions", + initial: &latest.ClusterDetails{ + Volumes: []latest.VolumeMount{ + { + ConfigMap: &latest.ConfigMapMount{ + Name: "kubernetes-config-map", + MountPath: "/mount-dir", + VolumeName: "kubernetes-config-map-volume", + Items: []latest.KeyToPath{ + { + Key: "config-map-file", + Path: "config-map/subpath", + Mode: &itemsFilePermissions, + }, + }, + }, + }, + }, + }, + artifact: &latest.KanikoArtifact{ + Image: "kaniko-latest", + }, + expected: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "kaniko-", + Labels: map[string]string{"skaffold-kaniko": "skaffold-kaniko"}, + }, + Spec: v1.PodSpec{ + RestartPolicy: "Never", + Containers: []v1.Container{ + { + Name: "kaniko", + Image: "kaniko-latest", + Env: []v1.EnvVar{{ + Name: "GOOGLE_APPLICATION_CREDENTIALS", + Value: "/secret/kaniko-secret", + }, { + Name: "UPSTREAM_CLIENT_TYPE", + Value: "UpstreamClient(skaffold-test)", + }}, + ImagePullPolicy: v1.PullPolicy("IfNotPresent"), + VolumeMounts: []v1.VolumeMount{ + { + Name: "kubernetes-config-map-volume", + MountPath: "/mount-dir", + }, + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "kubernetes-config-map-volume", + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "kubernetes-config-map", + }, + Items: []v1.KeyToPath{ + { + Key: "config-map-file", + Path: "config-map/subpath", + Mode: &itemsFilePermissions, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + description: "config-map volume with default and per-item permissions", + initial: &latest.ClusterDetails{ + Volumes: []latest.VolumeMount{ + { + ConfigMap: &latest.ConfigMapMount{ + Name: "kubernetes-config-map", + MountPath: "/mount-dir", + VolumeName: "kubernetes-config-map-volume", + DefaultMode: &defaultFilePermissions, + Items: []latest.KeyToPath{ + { + Key: "config-map-file-default-permissions", + Path: "config-map/subpath-default-perm", + }, + { + Key: "config-map-file-custom-permissions", + Path: "config-map/subpath-custom-perm", + Mode: &itemsFilePermissions, + }, + }, + }, + }, + }, + }, + artifact: &latest.KanikoArtifact{ + Image: "kaniko-latest", + }, + expected: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "kaniko-", + Labels: map[string]string{"skaffold-kaniko": "skaffold-kaniko"}, + }, + Spec: v1.PodSpec{ + RestartPolicy: "Never", + Containers: []v1.Container{ + { + Name: "kaniko", + Image: "kaniko-latest", + Env: []v1.EnvVar{{ + Name: "GOOGLE_APPLICATION_CREDENTIALS", + Value: "/secret/kaniko-secret", + }, { + Name: "UPSTREAM_CLIENT_TYPE", + Value: "UpstreamClient(skaffold-test)", + }}, + ImagePullPolicy: v1.PullPolicy("IfNotPresent"), + VolumeMounts: []v1.VolumeMount{ + { + Name: "kubernetes-config-map-volume", + MountPath: "/mount-dir", + }, + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "kubernetes-config-map-volume", + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "kubernetes-config-map", + }, + DefaultMode: &defaultFilePermissions, + Items: []v1.KeyToPath{ + { + Key: "config-map-file-default-permissions", + Path: "config-map/subpath-default-perm", + }, + { + Key: "config-map-file-custom-permissions", + Path: "config-map/subpath-custom-perm", + Mode: &itemsFilePermissions, + }, + }, + }, + }, + }, + }, + }, + }, + }, } opt := cmp.Comparer(func(x, y resource.Quantity) bool { diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 9e9badcd3c3..dc6c9edff3f 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -339,6 +339,10 @@ type ConfigMapMount struct { // VolumeName defines Kubernetes pod.spec.volumes[].name for the Pod. VolumeName string `yaml:"volumeName" yamltags:"required"` + + // DefaultMode UNIX mode bits to set on mounted files. Must be a + // value between 0 and 0777. Defaults to 0644. + DefaultMode *int32 `yaml:"defaultMode,omitempty"` } // SecretMount describes one Secret mount to a kaniko container filesystem. @@ -355,6 +359,10 @@ type SecretMount struct { // VolumeName defines Kubernetes pod.spec.volumes[].name for the Pod. VolumeName string `yaml:"volumeName" yamltags:"required"` + + // DefaultMode UNIX mode bits to set on mounted files. Must be a + // value between 0 and 0777. Defaults to 0644. + DefaultMode *int32 `yaml:"defaultMode,omitempty"` } // KeyToPath describes mapping from ConfigMap or Secret resource key to a path within a volume. @@ -364,6 +372,10 @@ type KeyToPath struct { // Path is the relative path to mount Key to. Path string `yaml:"path" yamltags:"required"` + + // Mode UNIX mode bits to set on mounted files. Must be a + // value between 0 and 0777. Defaults to 0644. + Mode *int32 `yaml:"mode,omitempty"` } // DockerConfig contains information about the docker `config.json` to mount. From 8cffe806b53a829ba6375909b5ae91c83ff341b5 Mon Sep 17 00:00:00 2001 From: Dmitriy Ermakov Date: Sun, 24 Nov 2019 01:05:44 +0300 Subject: [PATCH 08/10] Implements setting Environment Variables Signed-off-by: Dmitriy Ermakov --- docs/content/en/schemas/v2alpha1.json | 97 +++++++++- pkg/skaffold/build/cluster/sources/sources.go | 51 ++++++ .../build/cluster/sources/sources_test.go | 172 ++++++++++++++++++ pkg/skaffold/schema/latest/config.go | 36 ++++ 4 files changed, 355 insertions(+), 1 deletion(-) diff --git a/docs/content/en/schemas/v2alpha1.json b/docs/content/en/schemas/v2alpha1.json index 7fcf64b2d86..b22493f4c79 100755 --- a/docs/content/en/schemas/v2alpha1.json +++ b/docs/content/en/schemas/v2alpha1.json @@ -578,6 +578,14 @@ "description": "describes how to mount the local Docker configuration into a pod.", "x-intellij-html-description": "describes how to mount the local Docker configuration into a pod." }, + "envVars": { + "items": { + "$ref": "#/definitions/EnvVar" + }, + "type": "array", + "description": "defines Environment Variables for the containers in the Kaniko Pod.", + "x-intellij-html-description": "defines Environment Variables for the containers in the Kaniko Pod." + }, "namespace": { "type": "string", "description": "Kubernetes namespace. Defaults to current namespace in Kubernetes configuration.", @@ -629,12 +637,38 @@ "dockerConfig", "resources", "concurrency", - "volumes" + "volumes", + "envVars" ], "additionalProperties": false, "description": "*beta* describes how to do an on-cluster build.", "x-intellij-html-description": "beta describes how to do an on-cluster build." }, + "ConfigMapKeyRef": { + "required": [ + "name", + "key" + ], + "properties": { + "key": { + "type": "string", + "description": "sets the Key from the Secret to set as the Env Var value.", + "x-intellij-html-description": "sets the Key from the Secret to set as the Env Var value." + }, + "name": { + "type": "string", + "description": "sets the name of the Secret which is used to get value for the Env Var.", + "x-intellij-html-description": "sets the name of the Secret which is used to get value for the Env Var." + } + }, + "preferredOrder": [ + "name", + "key" + ], + "additionalProperties": false, + "description": "describes a ConfigMap source for Environent Variable value.", + "x-intellij-html-description": "describes a ConfigMap source for Environent Variable value." + }, "ConfigMapMount": { "required": [ "name", @@ -1014,6 +1048,42 @@ "description": "*beta* tags images with a configurable template string.", "x-intellij-html-description": "beta tags images with a configurable template string." }, + "EnvVar": { + "required": [ + "name" + ], + "properties": { + "configMap": { + "$ref": "#/definitions/ConfigMapKeyRef", + "description": "sets the given Kubernetes Config Map as a value for the Env Var.", + "x-intellij-html-description": "sets the given Kubernetes Config Map as a value for the Env Var." + }, + "name": { + "type": "string", + "description": "sets name for configured Environment Variable.", + "x-intellij-html-description": "sets name for configured Environment Variable." + }, + "secret": { + "$ref": "#/definitions/SecretKeyRef", + "description": "sets the given Kubernetes Secret as a value for the Env Var.", + "x-intellij-html-description": "sets the given Kubernetes Secret as a value for the Env Var." + }, + "value": { + "type": "string", + "description": "sets the Value for the Env Var.", + "x-intellij-html-description": "sets the Value for the Env Var." + } + }, + "preferredOrder": [ + "name", + "secret", + "configMap", + "value" + ], + "additionalProperties": false, + "description": "describes one Environment Variable to set to the Kaniko Pod.", + "x-intellij-html-description": "describes one Environment Variable to set to the Kaniko Pod." + }, "GitTagger": { "properties": { "variant": { @@ -1973,6 +2043,31 @@ "description": "describes the Kubernetes resource types used for port forwarding.", "x-intellij-html-description": "describes the Kubernetes resource types used for port forwarding." }, + "SecretKeyRef": { + "required": [ + "name", + "key" + ], + "properties": { + "key": { + "type": "string", + "description": "sets the Key from the Secret to set as the Env Var value.", + "x-intellij-html-description": "sets the Key from the Secret to set as the Env Var value." + }, + "name": { + "type": "string", + "description": "sets the name of the Secret which is used to get value for the Env Var.", + "x-intellij-html-description": "sets the name of the Secret which is used to get value for the Env Var." + } + }, + "preferredOrder": [ + "name", + "key" + ], + "additionalProperties": false, + "description": "describes a Secret source for Environent Variable value.", + "x-intellij-html-description": "describes a Secret source for Environent Variable value." + }, "SecretMount": { "required": [ "name", diff --git a/pkg/skaffold/build/cluster/sources/sources.go b/pkg/skaffold/build/cluster/sources/sources.go index a741c485b41..3c55d38b60e 100644 --- a/pkg/skaffold/build/cluster/sources/sources.go +++ b/pkg/skaffold/build/cluster/sources/sources.go @@ -113,6 +113,11 @@ func podTemplate(clusterDetails *latest.ClusterDetails, artifact *latest.KanikoA } } + // Add user-configured Environment Variables + for _, e := range clusterDetails.EnvVars { + addEnvVar(pod, e) + } + if clusterDetails.DockerConfig == nil { return pod } @@ -123,6 +128,52 @@ func podTemplate(clusterDetails *latest.ClusterDetails, artifact *latest.KanikoA return pod } +func addEnvVar(pod *v1.Pod, e latest.EnvVar) { + switch { + case e.Secret != nil: + env := v1.EnvVar{ + Name: e.Name, + ValueFrom: &v1.EnvVarSource{ + SecretKeyRef: &v1.SecretKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: e.Secret.Name, + }, + Key: e.Secret.Key, + }, + }, + } + + appendEnvVar(pod, env) + case e.ConfigMap != nil: + env := v1.EnvVar{ + Name: e.Name, + ValueFrom: &v1.EnvVarSource{ + ConfigMapKeyRef: &v1.ConfigMapKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: e.ConfigMap.Name, + }, + Key: e.ConfigMap.Key, + }, + }, + } + + appendEnvVar(pod, env) + case e.Value != nil: + env := v1.EnvVar{ + Name: e.Name, + Value: *e.Value, + } + + appendEnvVar(pod, env) + } +} + +func appendEnvVar(pod *v1.Pod, env v1.EnvVar) { + for i := range pod.Spec.Containers { + pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, env) + } +} + func addSecretVolume(pod *v1.Pod, name, mountPath, secretName string, items []latest.KeyToPath, defaultMode *int32) { // Create Secret items var kp []v1.KeyToPath diff --git a/pkg/skaffold/build/cluster/sources/sources_test.go b/pkg/skaffold/build/cluster/sources/sources_test.go index eb7d2110ec5..b2f829ce928 100644 --- a/pkg/skaffold/build/cluster/sources/sources_test.go +++ b/pkg/skaffold/build/cluster/sources/sources_test.go @@ -1063,3 +1063,175 @@ func TestConfigMapVolume(t *testing.T) { }) } } + +func TestUserEnvVar(t *testing.T) { + var testEnvVarValue = "my_env_var_value" + + tests := []struct { + description string + initial *latest.ClusterDetails + artifact *latest.KanikoArtifact + expected *v1.Pod + }{ + { + description: "Env Var Key-Value", + initial: &latest.ClusterDetails{ + EnvVars: []latest.EnvVar{ + { + Name: "MY_ENV_VAR_KEY", + Value: &testEnvVarValue, + }, + }, + }, + artifact: &latest.KanikoArtifact{ + Image: "kaniko-latest", + }, + expected: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "kaniko-", + Labels: map[string]string{"skaffold-kaniko": "skaffold-kaniko"}, + }, + Spec: v1.PodSpec{ + RestartPolicy: "Never", + Containers: []v1.Container{ + { + Name: "kaniko", + Image: "kaniko-latest", + Env: []v1.EnvVar{ + { + Name: "GOOGLE_APPLICATION_CREDENTIALS", + Value: "/secret/kaniko-secret", + }, + { + Name: "UPSTREAM_CLIENT_TYPE", + Value: "UpstreamClient(skaffold-test)", + }, + { + Name: "MY_ENV_VAR_KEY", + Value: testEnvVarValue, + }, + }, + ImagePullPolicy: v1.PullPolicy("IfNotPresent"), + }, + }, + }, + }, + }, + { + description: "Env Var Config Map Source", + initial: &latest.ClusterDetails{ + EnvVars: []latest.EnvVar{ + { + Name: "MY_ENV_VAR_KEY", + ConfigMap: &latest.ConfigMapKeyRef{ + Name: "my-config-map", + Key: "config-map-key", + }, + }, + }, + }, + artifact: &latest.KanikoArtifact{ + Image: "kaniko-latest", + }, + expected: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "kaniko-", + Labels: map[string]string{"skaffold-kaniko": "skaffold-kaniko"}, + }, + Spec: v1.PodSpec{ + RestartPolicy: "Never", + Containers: []v1.Container{ + { + Name: "kaniko", + Image: "kaniko-latest", + Env: []v1.EnvVar{ + { + Name: "GOOGLE_APPLICATION_CREDENTIALS", + Value: "/secret/kaniko-secret", + }, + { + Name: "UPSTREAM_CLIENT_TYPE", + Value: "UpstreamClient(skaffold-test)", + }, + { + Name: "MY_ENV_VAR_KEY", + ValueFrom: &v1.EnvVarSource{ + ConfigMapKeyRef: &v1.ConfigMapKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "my-config-map", + }, + Key: "config-map-key", + }, + }, + }, + }, + ImagePullPolicy: v1.PullPolicy("IfNotPresent"), + }, + }, + }, + }, + }, + { + description: "Env Var Secret Source", + initial: &latest.ClusterDetails{ + EnvVars: []latest.EnvVar{ + { + Name: "MY_ENV_VAR_KEY", + Secret: &latest.SecretKeyRef{ + Name: "my-secret", + Key: "secret-key", + }, + }, + }, + }, + artifact: &latest.KanikoArtifact{ + Image: "kaniko-latest", + }, + expected: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "kaniko-", + Labels: map[string]string{"skaffold-kaniko": "skaffold-kaniko"}, + }, + Spec: v1.PodSpec{ + RestartPolicy: "Never", + Containers: []v1.Container{ + { + Name: "kaniko", + Image: "kaniko-latest", + Env: []v1.EnvVar{ + { + Name: "GOOGLE_APPLICATION_CREDENTIALS", + Value: "/secret/kaniko-secret", + }, + { + Name: "UPSTREAM_CLIENT_TYPE", + Value: "UpstreamClient(skaffold-test)", + }, + { + Name: "MY_ENV_VAR_KEY", + ValueFrom: &v1.EnvVarSource{ + SecretKeyRef: &v1.SecretKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "my-secret", + }, + Key: "secret-key", + }, + }, + }, + }, + ImagePullPolicy: v1.PullPolicy("IfNotPresent"), + }, + }, + }, + }, + }, + } + + for _, test := range tests { + testutil.Run(t, test.description, func(t *testutil.T) { + actual := podTemplate(test.initial, test.artifact, nil, "test") + + t.CheckDeepEqual(test.expected, actual) + }) + } +} diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index dc6c9edff3f..96d415f8cc8 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -313,6 +313,9 @@ type ClusterDetails struct { // Volumes define container mounts for ConfigMap and Secret resources. Volumes []VolumeMount `yaml:"volumes,omitempty"` + + // EnvVars defines Environment Variables for the containers in the Kaniko Pod. + EnvVars []EnvVar `yaml:"envVars,omitempty"` } // VolumeMount represents a volume to mount to the kaniko container. @@ -378,6 +381,39 @@ type KeyToPath struct { Mode *int32 `yaml:"mode,omitempty"` } +// EnvVar describes one Environment Variable to set to the Kaniko Pod. +type EnvVar struct { + // Name sets name for configured Environment Variable. + Name string `yaml:"name" yamltags:"required"` + + // Secret sets the given Kubernetes Secret as a value for the Env Var. + Secret *SecretKeyRef `yaml:"secret" yamltags:"oneOf=envVarSource"` + + // ConfigMap sets the given Kubernetes Config Map as a value for the Env Var. + ConfigMap *ConfigMapKeyRef `yaml:"configMap" yamltags:"oneOf=envVarSource"` + + // Value sets the Value for the Env Var. + Value *string `yaml:"value" yamltags:"oneOf=envVarSource"` +} + +// SecretKeyRef describes a Secret source for Environent Variable value. +type SecretKeyRef struct { + // Name sets the name of the Secret which is used to get value for the Env Var. + Name string `yaml:"name" yamltags:"required"` + + // Key sets the Key from the Secret to set as the Env Var value. + Key string `yaml:"key" yamltags:"required"` +} + +// ConfigMapKeyRef describes a ConfigMap source for Environent Variable value. +type ConfigMapKeyRef struct { + // Name sets the name of the Secret which is used to get value for the Env Var. + Name string `yaml:"name" yamltags:"required"` + + // Key sets the Key from the Secret to set as the Env Var value. + Key string `yaml:"key" yamltags:"required"` +} + // DockerConfig contains information about the docker `config.json` to mount. type DockerConfig struct { // Path is the path to the docker `config.json`. From d9483fee3d59fd7508ca1e1a2628a3295866e21a Mon Sep 17 00:00:00 2001 From: Dmitriy Ermakov Date: Sat, 18 Jan 2020 09:55:56 +0300 Subject: [PATCH 09/10] PR #3287 resolved conflicts Signed-off-by: Dmitriy Ermakov --- pkg/skaffold/build/cluster/pod.go | 11 +++ pkg/skaffold/build/cluster/pod_test.go | 74 +++++++++++++++++ pkg/skaffold/schema/latest/config.go | 106 ++----------------------- 3 files changed, 90 insertions(+), 101 deletions(-) diff --git a/pkg/skaffold/build/cluster/pod.go b/pkg/skaffold/build/cluster/pod.go index 90b93007436..e6eec413fb5 100644 --- a/pkg/skaffold/build/cluster/pod.go +++ b/pkg/skaffold/build/cluster/pod.go @@ -93,6 +93,17 @@ func (b *Builder) podSpec(artifact *latest.KanikoArtifact, tag string) (*v1.Pod, addSecretVolume(pod, constants.DefaultKanikoDockerConfigSecretName, constants.DefaultKanikoDockerConfigPath, b.ClusterDetails.DockerConfig.SecretName) } + // Add used-defines Volumes + for _, v := range b.Volumes { + pod.Spec.Volumes = append(pod.Spec.Volumes, v) + } + + // Add user-defined VolumeMounts + for _, vm := range artifact.VolumeMounts { + pod.Spec.InitContainers[0].VolumeMounts = append(pod.Spec.InitContainers[0].VolumeMounts, vm) + pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, vm) + } + return pod, nil } diff --git a/pkg/skaffold/build/cluster/pod_test.go b/pkg/skaffold/build/cluster/pod_test.go index 63239808f9f..c5eabf7fe9e 100644 --- a/pkg/skaffold/build/cluster/pod_test.go +++ b/pkg/skaffold/build/cluster/pod_test.go @@ -164,6 +164,20 @@ func TestPodSpec(t *testing.T) { Name: "KEY", Value: "VALUE", }}, + VolumeMounts: []v1.VolumeMount{ + { + Name: "cm-volume-1", + ReadOnly: true, + MountPath: "/cm-test-mount-path", + SubPath: "/subpath", + }, + { + Name: "secret-volume-1", + ReadOnly: true, + MountPath: "/secret-test-mount-path", + SubPath: "/subpath", + }, + }, } builder := &Builder{ @@ -181,6 +195,26 @@ func TestPodSpec(t *testing.T) { CPU: "0.5", }, }, + Volumes: []v1.Volume{ + { + Name: "cm-volume-1", + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "cm-1", + }, + }, + }, + }, + { + Name: "secret-volume-1", + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: "secret-1", + }, + }, + }, + }, }, } pod, _ := builder.podSpec(artifact, "tag") @@ -199,6 +233,16 @@ func TestPodSpec(t *testing.T) { VolumeMounts: []v1.VolumeMount{{ Name: constants.DefaultKanikoEmptyDirName, MountPath: constants.DefaultKanikoEmptyDirMountPath, + }, { + Name: "cm-volume-1", + ReadOnly: true, + MountPath: "/cm-secret-mount-path", + SubPath: "/subpath", + }, { + Name: "secret-volume-1", + ReadOnly: true, + MountPath: "/secret-secret-mount-path", + SubPath: "/subpath", }}, Resources: v1.ResourceRequirements{ Requests: map[v1.ResourceName]resource.Quantity{ @@ -239,6 +283,18 @@ func TestPodSpec(t *testing.T) { Name: constants.DefaultKanikoSecretName, MountPath: "/secret", }, + { + Name: "cm-volume-1", + ReadOnly: true, + MountPath: "/cm-secret-mount-path", + SubPath: "/subpath", + }, + { + Name: "secret-volume-1", + ReadOnly: true, + MountPath: "/secret-secret-mount-path", + SubPath: "/subpath", + }, }, Resources: v1.ResourceRequirements{ Requests: map[v1.ResourceName]resource.Quantity{ @@ -265,6 +321,24 @@ func TestPodSpec(t *testing.T) { }, }, }, + { + Name: "cm-volume-1", + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "cm-1", + }, + }, + }, + }, + { + Name: "secret-volume-1", + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: "secret-1", + }, + }, + }, }, }, } diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 3d316eeaa42..1872a6e8dd4 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -305,107 +305,8 @@ type ClusterDetails struct { // Defaults to `0`. Concurrency int `yaml:"concurrency,omitempty"` - // Volumes define container mounts for ConfigMap and Secret resources. - Volumes []VolumeMount `yaml:"volumes,omitempty"` - - // EnvVars defines Environment Variables for the containers in the Kaniko Pod. - EnvVars []EnvVar `yaml:"envVars,omitempty"` -} - -// VolumeMount represents a volume to mount to the kaniko container. -// Only one of its members may be specified. -type VolumeMount struct { - // ConfigMap specifies a ConfigMap mount into the kaniko pod container. - ConfigMap *ConfigMapMount `yaml:"configMap,omitempty" yamltags:"oneOf=volumeType"` - - // Secret specifies a Secret mount into the kaniko pod container. - Secret *SecretMount `yaml:"secret,omitempty" yamltags:"oneOf=volumeType"` -} - -// ConfigMapMount describes one ConfigMap mount to the kaniko container filesystem. -type ConfigMapMount struct { - // Name is the Kubernetes ConfigMap name. - Name string `yaml:"name" yamltags:"required"` - - // Items if specified then only defined keys of the ConfigMap will be projected to the pod filesystem using relative paths as - // described in [volumes configMap](https://kubernetes.io/docs/concepts/storage/volumes/#configmap). - Items []KeyToPath `yaml:"items,omitempty"` - - // MountPath defines the path to mount the ConfigMap. - MountPath string `yaml:"mountPath" yamltags:"required"` - - // VolumeName defines Kubernetes pod.spec.volumes[].name for the Pod. - VolumeName string `yaml:"volumeName" yamltags:"required"` - - // DefaultMode UNIX mode bits to set on mounted files. Must be a - // value between 0 and 0777. Defaults to 0644. - DefaultMode *int32 `yaml:"defaultMode,omitempty"` -} - -// SecretMount describes one Secret mount to a kaniko container filesystem. -type SecretMount struct { - // Name is the Kubernetes Secret name. - Name string `yaml:"name" yamltags:"required"` - - // Items if specified then only defined keys of the Secret will be projected to the pod filesystem using relative paths as - // described in [volumes secret](https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets). - Items []KeyToPath `yaml:"items,omitempty"` - - // MountPath defines the path to mount the Secret. - MountPath string `yaml:"mountPath" yamltags:"required"` - - // VolumeName defines Kubernetes pod.spec.volumes[].name for the Pod. - VolumeName string `yaml:"volumeName" yamltags:"required"` - - // DefaultMode UNIX mode bits to set on mounted files. Must be a - // value between 0 and 0777. Defaults to 0644. - DefaultMode *int32 `yaml:"defaultMode,omitempty"` -} - -// KeyToPath describes mapping from ConfigMap or Secret resource key to a path within a volume. -type KeyToPath struct { - // Key is the key to get from the Resource. - Key string `yaml:"key" yamltags:"required"` - - // Path is the relative path to mount Key to. - Path string `yaml:"path" yamltags:"required"` - - // Mode UNIX mode bits to set on mounted files. Must be a - // value between 0 and 0777. Defaults to 0644. - Mode *int32 `yaml:"mode,omitempty"` -} - -// EnvVar describes one Environment Variable to set to the Kaniko Pod. -type EnvVar struct { - // Name sets name for configured Environment Variable. - Name string `yaml:"name" yamltags:"required"` - - // Secret sets the given Kubernetes Secret as a value for the Env Var. - Secret *SecretKeyRef `yaml:"secret" yamltags:"oneOf=envVarSource"` - - // ConfigMap sets the given Kubernetes Config Map as a value for the Env Var. - ConfigMap *ConfigMapKeyRef `yaml:"configMap" yamltags:"oneOf=envVarSource"` - - // Value sets the Value for the Env Var. - Value *string `yaml:"value" yamltags:"oneOf=envVarSource"` -} - -// SecretKeyRef describes a Secret source for Environent Variable value. -type SecretKeyRef struct { - // Name sets the name of the Secret which is used to get value for the Env Var. - Name string `yaml:"name" yamltags:"required"` - - // Key sets the Key from the Secret to set as the Env Var value. - Key string `yaml:"key" yamltags:"required"` -} - -// ConfigMapKeyRef describes a ConfigMap source for Environent Variable value. -type ConfigMapKeyRef struct { - // Name sets the name of the Secret which is used to get value for the Env Var. - Name string `yaml:"name" yamltags:"required"` - - // Key sets the Key from the Secret to set as the Env Var value. - Key string `yaml:"key" yamltags:"required"` + // Volume defines container mounts for ConfigMap and Secret resources. + Volumes []v1.Volume `yaml:"volumes,omitempty"` } // DockerConfig contains information about the docker `config.json` to mount. @@ -881,6 +782,9 @@ type KanikoArtifact struct { // SkipTLS skips TLS verification when pulling and pushing the image. SkipTLS bool `yaml:"skipTLS,omitempty"` + + // VolumeMounts are volume mounts passed to kaniko pod. + VolumeMounts []v1.VolumeMount `yaml:"volumeMounts,omitempty"` } // DockerArtifact describes an artifact built from a Dockerfile, From 5925e3cb306e4182bd009ea1a7d72c8e886efcbf Mon Sep 17 00:00:00 2001 From: Dmitriy Ermakov Date: Sat, 18 Jan 2020 10:42:40 +0300 Subject: [PATCH 10/10] fixes lint Travis-CI lint errors Signed-off-by: Dmitriy Ermakov --- docs/content/en/schemas/v2alpha1.json | 252 +------------------------- docs/content/en/schemas/v2alpha3.json | 20 +- pkg/skaffold/build/cluster/pod.go | 4 +- pkg/skaffold/schema/latest/config.go | 2 +- 4 files changed, 21 insertions(+), 257 deletions(-) diff --git a/docs/content/en/schemas/v2alpha1.json b/docs/content/en/schemas/v2alpha1.json index a18bebfd63b..803b21b0527 100755 --- a/docs/content/en/schemas/v2alpha1.json +++ b/docs/content/en/schemas/v2alpha1.json @@ -578,14 +578,6 @@ "description": "describes how to mount the local Docker configuration into a pod.", "x-intellij-html-description": "describes how to mount the local Docker configuration into a pod." }, - "envVars": { - "items": { - "$ref": "#/definitions/EnvVar" - }, - "type": "array", - "description": "defines Environment Variables for the containers in the Kaniko Pod.", - "x-intellij-html-description": "defines Environment Variables for the containers in the Kaniko Pod." - }, "namespace": { "type": "string", "description": "Kubernetes namespace. Defaults to current namespace in Kubernetes configuration.", @@ -616,14 +608,6 @@ "type": "string", "description": "amount of time (in seconds) that this build is allowed to run. Defaults to 20 minutes (`20m`).", "x-intellij-html-description": "amount of time (in seconds) that this build is allowed to run. Defaults to 20 minutes (20m)." - }, - "volumes": { - "items": { - "$ref": "#/definitions/VolumeMount" - }, - "type": "array", - "description": "define container mounts for ConfigMap and Secret resources.", - "x-intellij-html-description": "define container mounts for ConfigMap and Secret resources." } }, "preferredOrder": [ @@ -636,86 +620,12 @@ "timeout", "dockerConfig", "resources", - "concurrency", - "volumes", - "envVars" + "concurrency" ], "additionalProperties": false, "description": "*beta* describes how to do an on-cluster build.", "x-intellij-html-description": "beta describes how to do an on-cluster build." }, - "ConfigMapKeyRef": { - "required": [ - "name", - "key" - ], - "properties": { - "key": { - "type": "string", - "description": "sets the Key from the Secret to set as the Env Var value.", - "x-intellij-html-description": "sets the Key from the Secret to set as the Env Var value." - }, - "name": { - "type": "string", - "description": "sets the name of the Secret which is used to get value for the Env Var.", - "x-intellij-html-description": "sets the name of the Secret which is used to get value for the Env Var." - } - }, - "preferredOrder": [ - "name", - "key" - ], - "additionalProperties": false, - "description": "describes a ConfigMap source for Environent Variable value.", - "x-intellij-html-description": "describes a ConfigMap source for Environent Variable value." - }, - "ConfigMapMount": { - "required": [ - "name", - "mountPath", - "volumeName" - ], - "properties": { - "defaultMode": { - "type": "integer", - "description": "UNIX mode bits to set on mounted files. Must be a value between 0 and 0777. Defaults to 0644.", - "x-intellij-html-description": "UNIX mode bits to set on mounted files. Must be a value between 0 and 0777. Defaults to 0644." - }, - "items": { - "items": { - "$ref": "#/definitions/KeyToPath" - }, - "type": "array", - "description": "if specified then only defined keys of the ConfigMap will be projected to the pod filesystem using relative paths as described in [volumes configMap](https://kubernetes.io/docs/concepts/storage/volumes/#configmap).", - "x-intellij-html-description": "if specified then only defined keys of the ConfigMap will be projected to the pod filesystem using relative paths as described in volumes configMap." - }, - "mountPath": { - "type": "string", - "description": "defines the path to mount the ConfigMap.", - "x-intellij-html-description": "defines the path to mount the ConfigMap." - }, - "name": { - "type": "string", - "description": "Kubernetes ConfigMap name.", - "x-intellij-html-description": "Kubernetes ConfigMap name." - }, - "volumeName": { - "type": "string", - "description": "defines Kubernetes pod.spec.volumes[].name for the Pod.", - "x-intellij-html-description": "defines Kubernetes pod.spec.volumes[].name for the Pod." - } - }, - "preferredOrder": [ - "name", - "items", - "mountPath", - "volumeName", - "defaultMode" - ], - "additionalProperties": false, - "description": "describes one ConfigMap mount to the kaniko container filesystem.", - "x-intellij-html-description": "describes one ConfigMap mount to the kaniko container filesystem." - }, "CustomArtifact": { "properties": { "buildCommand": { @@ -1048,42 +958,6 @@ "description": "*beta* tags images with a configurable template string.", "x-intellij-html-description": "beta tags images with a configurable template string." }, - "EnvVar": { - "required": [ - "name" - ], - "properties": { - "configMap": { - "$ref": "#/definitions/ConfigMapKeyRef", - "description": "sets the given Kubernetes Config Map as a value for the Env Var.", - "x-intellij-html-description": "sets the given Kubernetes Config Map as a value for the Env Var." - }, - "name": { - "type": "string", - "description": "sets name for configured Environment Variable.", - "x-intellij-html-description": "sets name for configured Environment Variable." - }, - "secret": { - "$ref": "#/definitions/SecretKeyRef", - "description": "sets the given Kubernetes Secret as a value for the Env Var.", - "x-intellij-html-description": "sets the given Kubernetes Secret as a value for the Env Var." - }, - "value": { - "type": "string", - "description": "sets the Value for the Env Var.", - "x-intellij-html-description": "sets the Value for the Env Var." - } - }, - "preferredOrder": [ - "name", - "secret", - "configMap", - "value" - ], - "additionalProperties": false, - "description": "describes one Environment Variable to set to the Kaniko Pod.", - "x-intellij-html-description": "describes one Environment Variable to set to the Kaniko Pod." - }, "GitTagger": { "properties": { "variant": { @@ -1670,37 +1544,6 @@ "description": "configures Kaniko caching. If a cache is specified, Kaniko will use a remote cache which will speed up builds.", "x-intellij-html-description": "configures Kaniko caching. If a cache is specified, Kaniko will use a remote cache which will speed up builds." }, - "KeyToPath": { - "required": [ - "key", - "path" - ], - "properties": { - "key": { - "type": "string", - "description": "key to get from the Resource.", - "x-intellij-html-description": "key to get from the Resource." - }, - "mode": { - "type": "integer", - "description": "UNIX mode bits to set on mounted files. Must be a value between 0 and 0777. Defaults to 0644.", - "x-intellij-html-description": "UNIX mode bits to set on mounted files. Must be a value between 0 and 0777. Defaults to 0644." - }, - "path": { - "type": "string", - "description": "relative path to mount Key to.", - "x-intellij-html-description": "relative path to mount Key to." - } - }, - "preferredOrder": [ - "key", - "path", - "mode" - ], - "additionalProperties": false, - "description": "describes mapping from ConfigMap or Secret resource key to a path within a volume.", - "x-intellij-html-description": "describes mapping from ConfigMap or Secret resource key to a path within a volume." - }, "KubectlDeploy": { "properties": { "flags": { @@ -2051,78 +1894,6 @@ "description": "describes the Kubernetes resource types used for port forwarding.", "x-intellij-html-description": "describes the Kubernetes resource types used for port forwarding." }, - "SecretKeyRef": { - "required": [ - "name", - "key" - ], - "properties": { - "key": { - "type": "string", - "description": "sets the Key from the Secret to set as the Env Var value.", - "x-intellij-html-description": "sets the Key from the Secret to set as the Env Var value." - }, - "name": { - "type": "string", - "description": "sets the name of the Secret which is used to get value for the Env Var.", - "x-intellij-html-description": "sets the name of the Secret which is used to get value for the Env Var." - } - }, - "preferredOrder": [ - "name", - "key" - ], - "additionalProperties": false, - "description": "describes a Secret source for Environent Variable value.", - "x-intellij-html-description": "describes a Secret source for Environent Variable value." - }, - "SecretMount": { - "required": [ - "name", - "mountPath", - "volumeName" - ], - "properties": { - "defaultMode": { - "type": "integer", - "description": "UNIX mode bits to set on mounted files. Must be a value between 0 and 0777. Defaults to 0644.", - "x-intellij-html-description": "UNIX mode bits to set on mounted files. Must be a value between 0 and 0777. Defaults to 0644." - }, - "items": { - "items": { - "$ref": "#/definitions/KeyToPath" - }, - "type": "array", - "description": "if specified then only defined keys of the Secret will be projected to the pod filesystem using relative paths as described in [volumes secret](https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets).", - "x-intellij-html-description": "if specified then only defined keys of the Secret will be projected to the pod filesystem using relative paths as described in volumes secret." - }, - "mountPath": { - "type": "string", - "description": "defines the path to mount the Secret.", - "x-intellij-html-description": "defines the path to mount the Secret." - }, - "name": { - "type": "string", - "description": "Kubernetes Secret name.", - "x-intellij-html-description": "Kubernetes Secret name." - }, - "volumeName": { - "type": "string", - "description": "defines Kubernetes pod.spec.volumes[].name for the Pod.", - "x-intellij-html-description": "defines Kubernetes pod.spec.volumes[].name for the Pod." - } - }, - "preferredOrder": [ - "name", - "items", - "mountPath", - "volumeName", - "defaultMode" - ], - "additionalProperties": false, - "description": "describes one Secret mount to a kaniko container filesystem.", - "x-intellij-html-description": "describes one Secret mount to a kaniko container filesystem." - }, "ShaTagger": { "description": "*beta* tags images with their sha256 digest.", "x-intellij-html-description": "beta tags images with their sha256 digest." @@ -2332,27 +2103,6 @@ "additionalProperties": false, "description": "a list of structure tests to run on images that Skaffold builds.", "x-intellij-html-description": "a list of structure tests to run on images that Skaffold builds." - }, - "VolumeMount": { - "properties": { - "configMap": { - "$ref": "#/definitions/ConfigMapMount", - "description": "specifies a ConfigMap mount into the kaniko pod container.", - "x-intellij-html-description": "specifies a ConfigMap mount into the kaniko pod container." - }, - "secret": { - "$ref": "#/definitions/SecretMount", - "description": "specifies a Secret mount into the kaniko pod container.", - "x-intellij-html-description": "specifies a Secret mount into the kaniko pod container." - } - }, - "preferredOrder": [ - "configMap", - "secret" - ], - "additionalProperties": false, - "description": "represents a volume to mount to the kaniko container. Only one of its members may be specified.", - "x-intellij-html-description": "represents a volume to mount to the kaniko container. Only one of its members may be specified." } } } diff --git a/docs/content/en/schemas/v2alpha3.json b/docs/content/en/schemas/v2alpha3.json index e138f3fce3e..a419437c069 100755 --- a/docs/content/en/schemas/v2alpha3.json +++ b/docs/content/en/schemas/v2alpha3.json @@ -609,6 +609,13 @@ "type": "string", "description": "amount of time (in seconds) that this build is allowed to run. Defaults to 20 minutes (`20m`).", "x-intellij-html-description": "amount of time (in seconds) that this build is allowed to run. Defaults to 20 minutes (20m)." + }, + "volumes": { + "items": {}, + "type": "array", + "description": "defines container mounts for ConfigMap and Secret resources.", + "x-intellij-html-description": "defines container mounts for ConfigMap and Secret resources.", + "default": "[]" } }, "preferredOrder": [ @@ -621,7 +628,8 @@ "timeout", "dockerConfig", "resources", - "concurrency" + "concurrency", + "volumes" ], "additionalProperties": false, "description": "*beta* describes how to do an on-cluster build.", @@ -1423,6 +1431,13 @@ "type": "string", "description": "Dockerfile target name to build.", "x-intellij-html-description": "Dockerfile target name to build." + }, + "volumeMounts": { + "items": {}, + "type": "array", + "description": "volume mounts passed to kaniko pod.", + "x-intellij-html-description": "volume mounts passed to kaniko pod.", + "default": "[]" } }, "preferredOrder": [ @@ -1435,7 +1450,8 @@ "image", "cache", "reproducible", - "skipTLS" + "skipTLS", + "volumeMounts" ], "additionalProperties": false, "description": "describes an artifact built from a Dockerfile, with kaniko.", diff --git a/pkg/skaffold/build/cluster/pod.go b/pkg/skaffold/build/cluster/pod.go index e6eec413fb5..ce90b1c7163 100644 --- a/pkg/skaffold/build/cluster/pod.go +++ b/pkg/skaffold/build/cluster/pod.go @@ -94,9 +94,7 @@ func (b *Builder) podSpec(artifact *latest.KanikoArtifact, tag string) (*v1.Pod, } // Add used-defines Volumes - for _, v := range b.Volumes { - pod.Spec.Volumes = append(pod.Spec.Volumes, v) - } + pod.Spec.Volumes = append(pod.Spec.Volumes, b.Volumes...) // Add user-defined VolumeMounts for _, vm := range artifact.VolumeMounts { diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index 1872a6e8dd4..62152f9255d 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -305,7 +305,7 @@ type ClusterDetails struct { // Defaults to `0`. Concurrency int `yaml:"concurrency,omitempty"` - // Volume defines container mounts for ConfigMap and Secret resources. + // Volumes defines container mounts for ConfigMap and Secret resources. Volumes []v1.Volume `yaml:"volumes,omitempty"` }