Add different behavior for configmaps in overlay

This commit is contained in:
Jingfang Liu 2018-03-30 15:12:17 -07:00
parent 7fa986e55b
commit 7772932f7f
12 changed files with 224 additions and 97 deletions

View File

@ -132,6 +132,12 @@ type ConfigMap struct {
// hash(content of configmap). // hash(content of configmap).
Name string `json:"name,omitempty" yaml:"name,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"`
// Behavior of the configmap, must be one of create, merge and replace
// 'create': create a new one;
// 'replace': replace the existing one;
// 'merge': merge the existing one.
Behavior string `json:"behavior,omitempty" yaml:"behavior,omitempty"`
// DataSources for configmap. // DataSources for configmap.
DataSources `json:",inline,omitempty" yaml:",inline,omitempty"` DataSources `json:",inline,omitempty" yaml:",inline,omitempty"`
} }

View File

@ -33,6 +33,8 @@ import (
type Application interface { type Application interface {
// Resources computes and returns the resources for the app. // Resources computes and returns the resources for the app.
Resources() (resource.ResourceCollection, error) Resources() (resource.ResourceCollection, error)
// SemiResources computes and returns the resources without name hash and name reference for the app
SemiResources() (resource.ResourceCollection, error)
// RawResources computes and returns the raw resources from the manifest. // RawResources computes and returns the raw resources from the manifest.
// It contains resources from 1) untransformed resources from current manifest 2) transformed resources from sub packages // It contains resources from 1) untransformed resources from current manifest 2) transformed resources from sub packages
RawResources() (resource.ResourceCollection, error) RawResources() (resource.ResourceCollection, error)
@ -63,9 +65,28 @@ func New(loader loader.Loader) (Application, error) {
} }
// Resources computes and returns the resources from the manifest. // Resources computes and returns the resources from the manifest.
// The namehashing for configmap/secrets and resolving name reference is only done
// in the most top overlay once at the end of getting resources.
func (a *applicationImpl) Resources() (resource.ResourceCollection, error) { func (a *applicationImpl) Resources() (resource.ResourceCollection, error) {
res, err := a.SemiResources()
if err != nil {
return nil, err
}
t, err := a.getHashAndReferenceTransformer()
if err != nil {
return nil, err
}
err = t.Transform(res)
if err != nil {
return nil, err
}
return res, nil
}
// SemiResources computes and returns the resources without name hash and name reference for the app
func (a *applicationImpl) SemiResources() (resource.ResourceCollection, error) {
errs := &interror.ManifestErrors{} errs := &interror.ManifestErrors{}
raw, err := a.RawResources() raw, err := a.rawResources()
if err != nil { if err != nil {
errs.Append(err) errs.Append(err)
} }
@ -82,9 +103,8 @@ func (a *applicationImpl) Resources() (resource.ResourceCollection, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Only append hash for generated configmaps and secrets.
nht := transformers.NewNameHashTransformer() allRes, err := resource.MergeWithOverride(raw, res)
err = nht.Transform(res)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -98,37 +118,11 @@ func (a *applicationImpl) Resources() (resource.ResourceCollection, error) {
return nil, errs return nil, errs
} }
// Reindex the Raw Resources (resources from sub package and resources field of this package). t, err := a.getTransformer(patches)
// raw is a ResourceCollection (map) from <GVK, original name> to object with the transformed name.
// transRaw is a ResourceCollection (map) from <GVK, transformed name> to object with the transformed name.
transRaw := reindexResourceCollection(raw)
// allRes contains the resources that are indexed by the original names (old names).
// allTransRes contains the resources that are indexed by the transformed names (new names).
// allRes and allTransRes point to the same set of objects with new names.
allTransRes, err := resource.Merge(res, transRaw)
if err != nil { if err != nil {
return nil, err return nil, err
} }
allRes, err := resource.Merge(res, raw) err = t.Transform(allRes)
if err != nil {
return nil, err
}
ot, err := transformers.NewOverlayTransformer(patches)
if err != nil {
return nil, err
}
// Overlay transformer uses the ResourceCollection indexed by the original names.
err = ot.Transform(allRes)
if err != nil {
return nil, err
}
t, err := a.getTransformer()
if err != nil {
return nil, err
}
err = t.Transform(allTransRes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -137,7 +131,25 @@ func (a *applicationImpl) Resources() (resource.ResourceCollection, error) {
} }
// RawResources computes and returns the raw resources from the manifest. // RawResources computes and returns the raw resources from the manifest.
// The namehashing for configmap/secrets and resolving name reference is only done
// in the most top overlay once at the end of getting resources.
func (a *applicationImpl) RawResources() (resource.ResourceCollection, error) { func (a *applicationImpl) RawResources() (resource.ResourceCollection, error) {
res, err := a.rawResources()
if err != nil {
return nil, err
}
t, err := a.getHashAndReferenceTransformer()
if err != nil {
return nil, err
}
err = t.Transform(res)
if err != nil {
return nil, err
}
return res, nil
}
func (a *applicationImpl) rawResources() (resource.ResourceCollection, error) {
subAppResources, errs := a.subAppResources() subAppResources, errs := a.subAppResources()
resources, err := resource.NewFromResources(a.loader, a.manifest.Resources) resources, err := resource.NewFromResources(a.loader, a.manifest.Resources)
if err != nil { if err != nil {
@ -166,7 +178,7 @@ func (a *applicationImpl) subAppResources() (resource.ResourceCollection, *inter
continue continue
} }
// Gather all transformed resources from subpackages. // Gather all transformed resources from subpackages.
subAppResources, err := subapp.Resources() subAppResources, err := subapp.SemiResources()
if err != nil { if err != nil {
errs.Append(err) errs.Append(err)
continue continue
@ -181,13 +193,19 @@ func (a *applicationImpl) subAppResources() (resource.ResourceCollection, *inter
} }
// getTransformer generates the following transformers: // getTransformer generates the following transformers:
// 1) name prefix // 1) apply overlay
// 2) apply labels // 2) name prefix
// 3) apply annotations // 3) apply labels
// 4) update name reference // 4) apply annotations
func (a *applicationImpl) getTransformer() (transformers.Transformer, error) { func (a *applicationImpl) getTransformer(patches []*resource.Resource) (transformers.Transformer, error) {
ts := []transformers.Transformer{} ts := []transformers.Transformer{}
ot, err := transformers.NewOverlayTransformer(patches)
if err != nil {
return nil, err
}
ts = append(ts, ot)
npt, err := transformers.NewDefaultingNamePrefixTransformer(string(a.manifest.NamePrefix)) npt, err := transformers.NewDefaultingNamePrefixTransformer(string(a.manifest.NamePrefix))
if err != nil { if err != nil {
return nil, err return nil, err
@ -206,6 +224,17 @@ func (a *applicationImpl) getTransformer() (transformers.Transformer, error) {
} }
ts = append(ts, at) ts = append(ts, at)
return transformers.NewMultiTransformer(ts), nil
}
// getHashAndReferenceTransformer generates the following transformers:
// 1) name hash for configmap and secrests
// 2) apply name reference
func (a *applicationImpl) getHashAndReferenceTransformer() (transformers.Transformer, error) {
ts := []transformers.Transformer{}
nht := transformers.NewNameHashTransformer()
ts = append(ts, nht)
nrt, err := transformers.NewDefaultingNameReferenceTransformer() nrt, err := transformers.NewDefaultingNameReferenceTransformer()
if err != nil { if err != nil {
return nil, err return nil, err
@ -214,17 +243,6 @@ func (a *applicationImpl) getTransformer() (transformers.Transformer, error) {
return transformers.NewMultiTransformer(ts), nil return transformers.NewMultiTransformer(ts), nil
} }
// reindexResourceCollection returns a new instance of ResourceCollection which
// is indexed using the new name in the object.
func reindexResourceCollection(rc resource.ResourceCollection) resource.ResourceCollection {
result := resource.ResourceCollection{}
for gvkn, res := range rc {
gvkn.Name = res.Data.GetName()
result[gvkn] = res
}
return result
}
func unmarshal(y []byte, o interface{}) error { func unmarshal(y []byte, o interface{}) error {
j, err := yaml.YAMLToJSON(y) j, err := yaml.YAMLToJSON(y)
if err != nil { if err != nil {

View File

@ -121,7 +121,7 @@ func TestResources(t *testing.T) {
"apiVersion": "v1", "apiVersion": "v1",
"kind": "ConfigMap", "kind": "ConfigMap",
"metadata": map[string]interface{}{ "metadata": map[string]interface{}{
"name": "foo-literalConfigMap-h25f8c59t4", "name": "foo-literalConfigMap-mc92bgcbh5",
"labels": map[string]interface{}{ "labels": map[string]interface{}{
"app": "nginx", "app": "nginx",
}, },
@ -146,7 +146,7 @@ func TestResources(t *testing.T) {
"apiVersion": "v1", "apiVersion": "v1",
"kind": "Secret", "kind": "Secret",
"metadata": map[string]interface{}{ "metadata": map[string]interface{}{
"name": "foo-secret-2c9kh7fh8t", "name": "foo-secret-877fcfhgt5",
"labels": map[string]interface{}{ "labels": map[string]interface{}{
"app": "nginx", "app": "nginx",
}, },

View File

@ -39,12 +39,12 @@ diff -u -N /tmp/noop/extensions_v1beta1_Deployment_mungebot.yaml /tmp/transforme
+ valueFrom: + valueFrom:
+ configMapKeyRef: + configMapKeyRef:
+ key: somekey + key: somekey
+ name: test-infra-app-env-hf26mf2f2f + name: test-infra-app-env-bh449c299k
+ - name: BAR + - name: BAR
+ valueFrom: + valueFrom:
+ secretKeyRef: + secretKeyRef:
+ key: somekey + key: somekey
+ name: test-infra-app-tls-4d47hbbh9m + name: test-infra-app-tls-6hkmhf2224
- name: foo - name: foo
value: bar value: bar
- image: nginx - image: nginx
@ -56,9 +56,9 @@ diff -u -N /tmp/noop/extensions_v1beta1_Deployment_mungebot.yaml /tmp/transforme
+ - configMapRef: + - configMapRef:
+ name: someConfigMap + name: someConfigMap
+ - configMapRef: + - configMapRef:
+ name: test-infra-app-env-hf26mf2f2f + name: test-infra-app-env-bh449c299k
+ - secretRef: + - secretRef:
+ name: test-infra-app-tls-4d47hbbh9m + name: test-infra-app-tls-6hkmhf2224
+ image: busybox + image: busybox
+ name: busybox + name: busybox
+ volumeMounts: + volumeMounts:
@ -68,11 +68,11 @@ diff -u -N /tmp/noop/extensions_v1beta1_Deployment_mungebot.yaml /tmp/transforme
+ name: app-tls + name: app-tls
+ volumes: + volumes:
+ - configMap: + - configMap:
+ name: test-infra-app-env-hf26mf2f2f + name: test-infra-app-env-bh449c299k
+ name: app-env + name: app-env
+ - name: app-tls + - name: app-tls
+ secret: + secret:
+ secretName: test-infra-app-tls-4d47hbbh9m + secretName: test-infra-app-tls-6hkmhf2224
diff -u -N /tmp/noop/v1_ConfigMap_app-config.yaml /tmp/transformed/v1_ConfigMap_app-config.yaml diff -u -N /tmp/noop/v1_ConfigMap_app-config.yaml /tmp/transformed/v1_ConfigMap_app-config.yaml
--- /tmp/noop/v1_ConfigMap_app-config.yaml YYYY-MM-DD HH:MM:SS --- /tmp/noop/v1_ConfigMap_app-config.yaml YYYY-MM-DD HH:MM:SS
+++ /tmp/transformed/v1_ConfigMap_app-config.yaml YYYY-MM-DD HH:MM:SS +++ /tmp/transformed/v1_ConfigMap_app-config.yaml YYYY-MM-DD HH:MM:SS
@ -91,7 +91,7 @@ diff -u -N /tmp/noop/v1_ConfigMap_app-config.yaml /tmp/transformed/v1_ConfigMap_
+ app: mungebot + app: mungebot
+ org: kubernetes + org: kubernetes
+ repo: test-infra + repo: test-infra
+ name: test-infra-app-config-ht8ck65bcg + name: test-infra-app-config-hf5424hg8g
diff -u -N /tmp/noop/v1_ConfigMap_app-env.yaml /tmp/transformed/v1_ConfigMap_app-env.yaml diff -u -N /tmp/noop/v1_ConfigMap_app-env.yaml /tmp/transformed/v1_ConfigMap_app-env.yaml
--- /tmp/noop/v1_ConfigMap_app-env.yaml YYYY-MM-DD HH:MM:SS --- /tmp/noop/v1_ConfigMap_app-env.yaml YYYY-MM-DD HH:MM:SS
+++ /tmp/transformed/v1_ConfigMap_app-env.yaml YYYY-MM-DD HH:MM:SS +++ /tmp/transformed/v1_ConfigMap_app-env.yaml YYYY-MM-DD HH:MM:SS
@ -109,7 +109,7 @@ diff -u -N /tmp/noop/v1_ConfigMap_app-env.yaml /tmp/transformed/v1_ConfigMap_app
+ app: mungebot + app: mungebot
+ org: kubernetes + org: kubernetes
+ repo: test-infra + repo: test-infra
+ name: test-infra-app-env-hf26mf2f2f + name: test-infra-app-env-bh449c299k
diff -u -N /tmp/noop/v1_Secret_app-tls.yaml /tmp/transformed/v1_Secret_app-tls.yaml diff -u -N /tmp/noop/v1_Secret_app-tls.yaml /tmp/transformed/v1_Secret_app-tls.yaml
--- /tmp/noop/v1_Secret_app-tls.yaml YYYY-MM-DD HH:MM:SS --- /tmp/noop/v1_Secret_app-tls.yaml YYYY-MM-DD HH:MM:SS
+++ /tmp/transformed/v1_Secret_app-tls.yaml YYYY-MM-DD HH:MM:SS +++ /tmp/transformed/v1_Secret_app-tls.yaml YYYY-MM-DD HH:MM:SS
@ -127,7 +127,7 @@ diff -u -N /tmp/noop/v1_Secret_app-tls.yaml /tmp/transformed/v1_Secret_app-tls.y
+ app: mungebot + app: mungebot
+ org: kubernetes + org: kubernetes
+ repo: test-infra + repo: test-infra
+ name: test-infra-app-tls-4d47hbbh9m + name: test-infra-app-tls-6hkmhf2224
+type: kubernetes.io/tls +type: kubernetes.io/tls
diff -u -N /tmp/noop/v1_Service_mungebot-service.yaml /tmp/transformed/v1_Service_mungebot-service.yaml diff -u -N /tmp/noop/v1_Service_mungebot-service.yaml /tmp/transformed/v1_Service_mungebot-service.yaml
--- /tmp/noop/v1_Service_mungebot-service.yaml YYYY-MM-DD HH:MM:SS --- /tmp/noop/v1_Service_mungebot-service.yaml YYYY-MM-DD HH:MM:SS

View File

@ -12,7 +12,7 @@ metadata:
app: mungebot app: mungebot
org: kubernetes org: kubernetes
repo: test-infra repo: test-infra
name: test-infra-app-config-ht8ck65bcg name: test-infra-app-config-hf5424hg8g
--- ---
apiVersion: v1 apiVersion: v1
data: data:
@ -27,7 +27,7 @@ metadata:
app: mungebot app: mungebot
org: kubernetes org: kubernetes
repo: test-infra repo: test-infra
name: test-infra-app-env-hf26mf2f2f name: test-infra-app-env-bh449c299k
--- ---
apiVersion: v1 apiVersion: v1
data: data:
@ -42,7 +42,7 @@ metadata:
app: mungebot app: mungebot
org: kubernetes org: kubernetes
repo: test-infra repo: test-infra
name: test-infra-app-tls-4d47hbbh9m name: test-infra-app-tls-6hkmhf2224
type: kubernetes.io/tls type: kubernetes.io/tls
--- ---
apiVersion: v1 apiVersion: v1
@ -103,12 +103,12 @@ spec:
valueFrom: valueFrom:
configMapKeyRef: configMapKeyRef:
key: somekey key: somekey
name: test-infra-app-env-hf26mf2f2f name: test-infra-app-env-bh449c299k
- name: BAR - name: BAR
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
key: somekey key: somekey
name: test-infra-app-tls-4d47hbbh9m name: test-infra-app-tls-6hkmhf2224
- name: foo - name: foo
value: bar value: bar
image: nginx:1.7.9 image: nginx:1.7.9
@ -119,9 +119,9 @@ spec:
- configMapRef: - configMapRef:
name: someConfigMap name: someConfigMap
- configMapRef: - configMapRef:
name: test-infra-app-env-hf26mf2f2f name: test-infra-app-env-bh449c299k
- secretRef: - secretRef:
name: test-infra-app-tls-4d47hbbh9m name: test-infra-app-tls-6hkmhf2224
image: busybox image: busybox
name: busybox name: busybox
volumeMounts: volumeMounts:
@ -131,8 +131,8 @@ spec:
name: app-tls name: app-tls
volumes: volumes:
- configMap: - configMap:
name: test-infra-app-env-hf26mf2f2f name: test-infra-app-env-bh449c299k
name: app-env name: app-env
- name: app-tls - name: app-tls
secret: secret:
secretName: test-infra-app-tls-4d47hbbh9m secretName: test-infra-app-tls-6hkmhf2224

View File

@ -1,14 +1,15 @@
diff -u -N /tmp/noop/apps_v1beta2_Deployment_nginx.yaml /tmp/transformed/apps_v1beta2_Deployment_nginx.yaml diff -u -N /tmp/noop/apps_v1beta2_Deployment_nginx.yaml /tmp/transformed/apps_v1beta2_Deployment_nginx.yaml
--- /tmp/noop/apps_v1beta2_Deployment_nginx.yaml YYYY-MM-DD HH:MM:SS --- /tmp/noop/apps_v1beta2_Deployment_nginx.yaml YYYY-MM-DD HH:MM:SS
+++ /tmp/transformed/apps_v1beta2_Deployment_nginx.yaml YYYY-MM-DD HH:MM:SS +++ /tmp/transformed/apps_v1beta2_Deployment_nginx.yaml YYYY-MM-DD HH:MM:SS
@@ -5,13 +5,15 @@ @@ -5,23 +5,26 @@
note: This is a test annotation note: This is a test annotation
labels: labels:
app: mynginx app: mynginx
+ env: staging + env: staging
org: example.com org: example.com
team: foo - team: foo
- name: team-foo-nginx - name: team-foo-nginx
+ team: override-foo
+ name: staging-team-foo-nginx + name: staging-team-foo-nginx
spec: spec:
selector: selector:
@ -16,16 +17,21 @@ diff -u -N /tmp/noop/apps_v1beta2_Deployment_nginx.yaml /tmp/transformed/apps_v1
app: mynginx app: mynginx
+ env: staging + env: staging
org: example.com org: example.com
team: foo - team: foo
+ team: override-foo
template: template:
@@ -20,6 +22,7 @@ metadata:
annotations:
note: This is a test annotation note: This is a test annotation
labels: labels:
app: mynginx app: mynginx
+ env: staging + env: staging
org: example.com org: example.com
team: foo - team: foo
+ team: override-foo
spec: spec:
containers:
- image: nginx
@@ -30,8 +33,12 @@ @@ -30,8 +33,12 @@
- mountPath: /tmp/ps - mountPath: /tmp/ps
name: nginx-persistent-storage name: nginx-persistent-storage
@ -35,28 +41,37 @@ diff -u -N /tmp/noop/apps_v1beta2_Deployment_nginx.yaml /tmp/transformed/apps_v1
+ pdName: nginx-persistent-storage + pdName: nginx-persistent-storage
name: nginx-persistent-storage name: nginx-persistent-storage
- configMap: - configMap:
- name: team-foo-configmap-in-base-72t84tc949 - name: team-foo-configmap-in-base-bbdmdh7m8t
+ name: staging-configmap-in-overlay-h4hbb8fckf + name: staging-configmap-in-overlay-k7cbc75tg8
+ name: configmap-in-overlay + name: configmap-in-overlay
+ - configMap: + - configMap:
+ name: staging-team-foo-configmap-in-base-72t84tc949 + name: staging-team-foo-configmap-in-base-gh9d7t85gb
name: configmap-in-base name: configmap-in-base
diff -u -N /tmp/noop/v1_ConfigMap_configmap-in-base.yaml /tmp/transformed/v1_ConfigMap_configmap-in-base.yaml diff -u -N /tmp/noop/v1_ConfigMap_configmap-in-base.yaml /tmp/transformed/v1_ConfigMap_configmap-in-base.yaml
--- /tmp/noop/v1_ConfigMap_configmap-in-base.yaml YYYY-MM-DD HH:MM:SS --- /tmp/noop/v1_ConfigMap_configmap-in-base.yaml YYYY-MM-DD HH:MM:SS
+++ /tmp/transformed/v1_ConfigMap_configmap-in-base.yaml YYYY-MM-DD HH:MM:SS +++ /tmp/transformed/v1_ConfigMap_configmap-in-base.yaml YYYY-MM-DD HH:MM:SS
@@ -1,6 +1,6 @@
apiVersion: v1
data:
- foo: bar
+ foo: override-bar
kind: ConfigMap
metadata:
annotations:
@@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
creationTimestamp: null creationTimestamp: null
labels: labels:
app: mynginx app: mynginx
+ env: staging + env: staging
org: example.com org: example.com
team: foo - team: foo
- name: team-foo-configmap-in-base-72t84tc949 - name: team-foo-configmap-in-base-bbdmdh7m8t
+ name: staging-team-foo-configmap-in-base-72t84tc949 + team: override-foo
+ name: staging-team-foo-configmap-in-base-gh9d7t85gb
diff -u -N /tmp/noop/v1_ConfigMap_configmap-in-overlay.yaml /tmp/transformed/v1_ConfigMap_configmap-in-overlay.yaml diff -u -N /tmp/noop/v1_ConfigMap_configmap-in-overlay.yaml /tmp/transformed/v1_ConfigMap_configmap-in-overlay.yaml
--- /tmp/noop/v1_ConfigMap_configmap-in-overlay.yaml YYYY-MM-DD HH:MM:SS --- /tmp/noop/v1_ConfigMap_configmap-in-overlay.yaml YYYY-MM-DD HH:MM:SS
+++ /tmp/transformed/v1_ConfigMap_configmap-in-overlay.yaml YYYY-MM-DD HH:MM:SS +++ /tmp/transformed/v1_ConfigMap_configmap-in-overlay.yaml YYYY-MM-DD HH:MM:SS
@@ -0,0 +1,9 @@ @@ -0,0 +1,10 @@
+apiVersion: v1 +apiVersion: v1
+data: +data:
+ hello: world + hello: world
@ -65,7 +80,8 @@ diff -u -N /tmp/noop/v1_ConfigMap_configmap-in-overlay.yaml /tmp/transformed/v1_
+ creationTimestamp: null + creationTimestamp: null
+ labels: + labels:
+ env: staging + env: staging
+ name: staging-configmap-in-overlay-h4hbb8fckf + team: override-foo
+ name: staging-configmap-in-overlay-k7cbc75tg8
diff -u -N /tmp/noop/v1_Service_nginx.yaml /tmp/transformed/v1_Service_nginx.yaml diff -u -N /tmp/noop/v1_Service_nginx.yaml /tmp/transformed/v1_Service_nginx.yaml
--- /tmp/noop/v1_Service_nginx.yaml YYYY-MM-DD HH:MM:SS --- /tmp/noop/v1_Service_nginx.yaml YYYY-MM-DD HH:MM:SS
+++ /tmp/transformed/v1_Service_nginx.yaml YYYY-MM-DD HH:MM:SS +++ /tmp/transformed/v1_Service_nginx.yaml YYYY-MM-DD HH:MM:SS
@ -75,8 +91,9 @@ diff -u -N /tmp/noop/v1_Service_nginx.yaml /tmp/transformed/v1_Service_nginx.yam
app: mynginx app: mynginx
+ env: staging + env: staging
org: example.com org: example.com
team: foo - team: foo
- name: team-foo-nginx - name: team-foo-nginx
+ team: override-foo
+ name: staging-team-foo-nginx + name: staging-team-foo-nginx
spec: spec:
ports: ports:
@ -85,4 +102,5 @@ diff -u -N /tmp/noop/v1_Service_nginx.yaml /tmp/transformed/v1_Service_nginx.yam
app: mynginx app: mynginx
+ env: staging + env: staging
org: example.com org: example.com
team: foo - team: foo
+ team: override-foo

View File

@ -1,6 +1,6 @@
apiVersion: v1 apiVersion: v1
data: data:
foo: bar foo: override-bar
kind: ConfigMap kind: ConfigMap
metadata: metadata:
annotations: annotations:
@ -10,8 +10,8 @@ metadata:
app: mynginx app: mynginx
env: staging env: staging
org: example.com org: example.com
team: foo team: override-foo
name: staging-team-foo-configmap-in-base-72t84tc949 name: staging-team-foo-configmap-in-base-gh9d7t85gb
--- ---
apiVersion: v1 apiVersion: v1
data: data:
@ -21,7 +21,8 @@ metadata:
creationTimestamp: null creationTimestamp: null
labels: labels:
env: staging env: staging
name: staging-configmap-in-overlay-h4hbb8fckf team: override-foo
name: staging-configmap-in-overlay-k7cbc75tg8
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
@ -32,7 +33,7 @@ metadata:
app: mynginx app: mynginx
env: staging env: staging
org: example.com org: example.com
team: foo team: override-foo
name: staging-team-foo-nginx name: staging-team-foo-nginx
spec: spec:
ports: ports:
@ -41,7 +42,7 @@ spec:
app: mynginx app: mynginx
env: staging env: staging
org: example.com org: example.com
team: foo team: override-foo
--- ---
apiVersion: apps/v1beta2 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
@ -52,7 +53,7 @@ metadata:
app: mynginx app: mynginx
env: staging env: staging
org: example.com org: example.com
team: foo team: override-foo
name: staging-team-foo-nginx name: staging-team-foo-nginx
spec: spec:
selector: selector:
@ -60,7 +61,7 @@ spec:
app: mynginx app: mynginx
env: staging env: staging
org: example.com org: example.com
team: foo team: override-foo
template: template:
metadata: metadata:
annotations: annotations:
@ -69,7 +70,7 @@ spec:
app: mynginx app: mynginx
env: staging env: staging
org: example.com org: example.com
team: foo team: override-foo
spec: spec:
containers: containers:
- image: nginx - image: nginx
@ -82,8 +83,8 @@ spec:
pdName: nginx-persistent-storage pdName: nginx-persistent-storage
name: nginx-persistent-storage name: nginx-persistent-storage
- configMap: - configMap:
name: staging-configmap-in-overlay-h4hbb8fckf name: staging-configmap-in-overlay-k7cbc75tg8
name: configmap-in-overlay name: configmap-in-overlay
- configMap: - configMap:
name: staging-team-foo-configmap-in-base-72t84tc949 name: staging-team-foo-configmap-in-base-gh9d7t85gb
name: configmap-in-base name: configmap-in-base

View File

@ -5,6 +5,7 @@ metadata:
namePrefix: staging- namePrefix: staging-
objectLabels: objectLabels:
env: staging env: staging
team: override-foo
patches: patches:
- deployment.yaml - deployment.yaml
bases: bases:
@ -13,3 +14,7 @@ configmaps:
- name: configmap-in-overlay - name: configmap-in-overlay
literals: literals:
- hello=world - hello=world
- name: configmap-in-base
behavior: replace
literals:
- foo=override-bar

View File

@ -19,3 +19,8 @@ package constants
// KubeManifestFileName is the Well-Known File Name for a kubernetes app manifest. // KubeManifestFileName is the Well-Known File Name for a kubernetes app manifest.
const KubeManifestSuffix = ".yaml" const KubeManifestSuffix = ".yaml"
const KubeManifestFileName = "Kube-manifest" + KubeManifestSuffix const KubeManifestFileName = "Kube-manifest" + KubeManifestSuffix
// Configmap behaviors
const CreateBehavior = "create"
const ReplaceBehavior = "replace"
const MergeBehavior = "merge"

View File

@ -37,7 +37,7 @@ func newFromConfigMap(l loader.Loader, cm manifest.ConfigMap) (*Resource, error)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &Resource{Data: data}, nil return &Resource{Data: data, Behavior: cm.Behavior}, nil
} }
func makeConfigMap(l loader.Loader, cm manifest.ConfigMap) (*corev1.ConfigMap, error) { func makeConfigMap(l loader.Loader, cm manifest.ConfigMap) (*corev1.ConfigMap, error) {

View File

@ -28,6 +28,7 @@ import (
// ConfigMap etc. // ConfigMap etc.
type Resource struct { type Resource struct {
Data *unstructured.Unstructured Data *unstructured.Unstructured
Behavior string
} }
// GVKN returns Group/Version/Kind/Name for the resource. // GVKN returns Group/Version/Kind/Name for the resource.

View File

@ -23,6 +23,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
k8syaml "k8s.io/apimachinery/pkg/util/yaml" k8syaml "k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/kubectl/pkg/kinflate/constants"
) )
// decode decodes a list of objects in byte array format // decode decodes a list of objects in byte array format
@ -90,3 +91,75 @@ func Merge(rcs ...ResourceCollection) (ResourceCollection, error) {
return all, nil return all, nil
} }
// MergeWithOverride will merge all of the entries in the slice of ResourceCollection with Override
// If there is already an entry with the same GVKN exists, different actions are performed according to value of Behavior field
// 'create': create a new one;
// 'replace': replace the data only; keep the labels and annotations
// 'merge': merge the data; keep the labels and annotations
func MergeWithOverride(rcs ...ResourceCollection) (ResourceCollection, error) {
all := ResourceCollection{}
for _, rc := range rcs {
for gvkn, obj := range rc {
if _, found := all[gvkn]; found {
switch obj.Behavior {
case "", constants.CreateBehavior:
return nil, fmt.Errorf("Create an existing gvkn %#v is not allowed", gvkn)
case constants.ReplaceBehavior:
obj.replace(all[gvkn])
all[gvkn] = obj
case constants.MergeBehavior:
obj.merge(all[gvkn])
all[gvkn] = obj
default:
return nil, fmt.Errorf("The behavior of %#v must be one of merge and replace since it already exists in the base", gvkn)
}
} else {
switch obj.Behavior {
case "", constants.CreateBehavior:
all[gvkn] = obj
case constants.MergeBehavior, constants.ReplaceBehavior:
return nil, fmt.Errorf("No merge or replace is allowed for non existing gvkn %#v", gvkn)
default:
return nil, fmt.Errorf("The behavior of %#v must be create since it doesn't exist", gvkn)
}
}
}
}
return all, nil
}
func (r *Resource) replace(other *Resource) {
r.Data.SetLabels(mergeMap(other.Data.GetLabels(), r.Data.GetLabels()))
r.Data.SetAnnotations(mergeMap(other.Data.GetAnnotations(), r.Data.GetAnnotations()))
r.Data.SetName(other.Data.GetName())
}
func (r *Resource) merge(other *Resource) {
r.replace(other)
mergeConfigmap(r.Data.Object, other.Data.Object, r.Data.Object)
}
func mergeMap(maps ...map[string]string) map[string]string {
mergedMap := map[string]string{}
for _, m := range maps {
for key, value := range m {
mergedMap[key] = value
}
}
return mergedMap
}
// TODO: Add BinaryData once we sync to new k8s.io/api
func mergeConfigmap(mergedTo map[string]interface{}, maps ...map[string]interface{}) {
mergedMap := map[string]interface{}{}
for _, m := range maps {
datamap, ok := m["data"].(map[string]interface{})
if ok {
for key, value := range datamap {
mergedMap[key] = value
}
}
}
mergedTo["data"] = mergedMap
}