add util to update name reference

This commit is contained in:
ymqytw 2018-01-22 15:48:21 -08:00
parent f9dd21c734
commit fc54207123
4 changed files with 649 additions and 0 deletions

View File

@ -0,0 +1,122 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"errors"
"fmt"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// NameReferenceTransformer contains the referencing info between 2 GroupVersionKinds
type NameReferenceTransformer struct {
pathConfigs []referencePathConfig
}
var _ Transformer = &NameReferenceTransformer{}
// NewDefaultingNameReferenceTransformer constructs a NameReferenceTransformer
// with defaultNameReferencepathConfigs.
func NewDefaultingNameReferenceTransformer() (Transformer, error) {
return NewNameReferenceTransformer(defaultNameReferencePathConfigs)
}
// NewNameReferenceTransformer construct a NameReferenceTransformer.
func NewNameReferenceTransformer(pc []referencePathConfig) (Transformer, error) {
if pc == nil {
return nil, errors.New("pathConfigs is not expected to be nil")
}
return &NameReferenceTransformer{pathConfigs: pc}, nil
}
// Transform does the fields update according to pathConfigs.
// The old name is in the key in the map and the new name is in the object
// associated with the key. e.g. if <k, v> is one of the key-value pair in the map,
// then the old name is k.Name and the new name is v.GetName()
func (o *NameReferenceTransformer) Transform(
m map[GroupVersionKindName]*unstructured.Unstructured) error {
for GVKn := range m {
obj := m[GVKn]
objMap := obj.UnstructuredContent()
for _, referencePathConfig := range o.pathConfigs {
for _, path := range referencePathConfig.pathConfigs {
if !SelectByGVK(GVKn.GVK, path.GroupVersionKind) {
continue
}
err := mutateField(objMap, path.Path, path.CreateIfNotPresent,
o.updateNameReference(referencePathConfig.referencedGVK, m))
// Ignore the error when we can't find the GVKN that is being
// referenced, because the missing GVKN may be not included in
// this manifest and will be created later.
if IsNoMatchingGVKNError(err) {
continue
}
if err != nil {
return err
}
}
}
}
return nil
}
// NoMatchingGVKNError indicates failing to find a GroupVersionKindName.
type NoMatchingGVKNError struct {
message string
}
// NewNoMatchingGVKNError constructs an instance of NoMatchingGVKNError with
// a given error message.
func NewNoMatchingGVKNError(errMsg string) NoMatchingGVKNError {
return NoMatchingGVKNError{errMsg}
}
// IsNoMatchingGVKNError checks if the error is NoMatchingGVKNError type.
func IsNoMatchingGVKNError(err error) bool {
_, ok := err.(NoMatchingGVKNError)
return ok
}
// Error returns the error in string format.
func (err NoMatchingGVKNError) Error() string {
return err.message
}
func (o *NameReferenceTransformer) updateNameReference(
GVK schema.GroupVersionKind,
m map[GroupVersionKindName]*unstructured.Unstructured,
) func(in interface{}) (interface{}, error) {
return func(in interface{}) (interface{}, error) {
s, ok := in.(string)
if !ok {
return nil, fmt.Errorf("%#v is expectd to be %T", in, s)
}
for GVKn, obj := range m {
if !SelectByGVK(GVKn.GVK, &GVK) {
continue
}
if GVKn.Name == s {
return obj.GetName(), nil
}
}
return nil, NewNoMatchingGVKNError(
fmt.Sprintf("no matching for GroupVersionKind %v and Name %v", GVK, s))
}
}

View File

@ -0,0 +1,164 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"reflect"
"testing"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
)
func makeReferencedConfigMap() *unstructured.Unstructured {
return &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "someprefix-cm1-somehash",
},
},
}
}
func makeReferencedSecret() *unstructured.Unstructured {
return &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": "someprefix-secret1-somehash",
},
},
}
}
func makeNameRefDeployment(cmName, secretName string) *unstructured.Unstructured {
return &unstructured.Unstructured{
Object: map[string]interface{}{
"group": "apps",
"apiVersion": "v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "deploy1",
},
"spec": map[string]interface{}{
"template": map[string]interface{}{
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
"env": []interface{}{
map[string]interface{}{
"name": "CM_FOO",
"valueFrom": map[string]interface{}{
"configMapKeyRef": map[string]interface{}{
"name": cmName,
"key": "somekey",
},
},
},
map[string]interface{}{
"name": "SECRET_FOO",
"valueFrom": map[string]interface{}{
"secretKeyRef": map[string]interface{}{
"name": secretName,
"key": "somekey",
},
},
},
},
"envFrom": []interface{}{
map[string]interface{}{
"configMapRef": map[string]interface{}{
"name": cmName,
"key": "somekey",
},
},
map[string]interface{}{
"secretRef": map[string]interface{}{
"name": secretName,
"key": "somekey",
},
},
},
},
},
"volumes": map[string]interface{}{
"configMap": map[string]interface{}{
"name": cmName,
},
"secret": map[string]interface{}{
"name": secretName,
},
},
},
},
},
},
}
}
func makeNameReferenceTestMap() map[GroupVersionKindName]*unstructured.Unstructured {
return map[GroupVersionKindName]*unstructured.Unstructured{
{
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
Name: "cm1",
}: makeReferencedConfigMap(),
{
GVK: schema.GroupVersionKind{Version: "v1", Kind: "Secret"},
Name: "secret1",
}: makeReferencedSecret(),
{
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
Name: "deploy1",
}: makeNameRefDeployment("cm1", "secret1"),
}
}
func makeNameReferenceUpdatedTestMap() map[GroupVersionKindName]*unstructured.Unstructured {
return map[GroupVersionKindName]*unstructured.Unstructured{
{
GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
Name: "cm1",
}: makeReferencedConfigMap(),
{
GVK: schema.GroupVersionKind{Version: "v1", Kind: "Secret"},
Name: "secret1",
}: makeReferencedSecret(),
{
GVK: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
Name: "deploy1",
}: makeNameRefDeployment("someprefix-cm1-somehash", "someprefix-secret1-somehash"),
}
}
func TestNameReferenceRun(t *testing.T) {
m := makeNameReferenceTestMap()
nrt, err := NewDefaultingNameReferenceTransformer()
err = nrt.Transform(m)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expected := makeNameReferenceUpdatedTestMap()
if !reflect.DeepEqual(m, expected) {
err = compareMap(m, expected)
t.Fatalf("actual doesn't match expected: %v", err)
}
}

View File

@ -0,0 +1,342 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"k8s.io/apimachinery/pkg/runtime/schema"
)
// defaultNameReferencePathConfigs is the default configuration for updating
// the fields reference the name of other resources.
var defaultNameReferencePathConfigs = []referencePathConfig{
{
referencedGVK: schema.GroupVersionKind{
Version: "v1",
Kind: "ConfigMap",
},
pathConfigs: []PathConfig{
{
GroupVersionKind: &schema.GroupVersionKind{
Version: "v1",
Kind: "Pod",
},
Path: []string{"spec", "volumes", "configMap", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Version: "v1",
Kind: "Pod",
},
Path: []string{"spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Version: "v1",
Kind: "Pod",
},
Path: []string{"spec", "containers", "envFrom", "configMapRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "Deployment",
},
Path: []string{"spec", "template", "spec", "volumes", "configMap", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "Deployment",
},
Path: []string{"spec", "template", "spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "Deployment",
},
Path: []string{"spec", "template", "spec", "containers", "envFrom", "configMapRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "ReplicaSet",
},
Path: []string{"spec", "template", "spec", "volumes", "configMap", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "ReplicaSet",
},
Path: []string{"spec", "template", "spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "ReplicaSet",
},
Path: []string{"spec", "template", "spec", "containers", "envFrom", "configMapRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "DaemonSet",
},
Path: []string{"spec", "template", "spec", "volumes", "configMap", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "DaemonSet",
},
Path: []string{"spec", "template", "spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "DaemonSet",
},
Path: []string{"spec", "template", "spec", "containers", "envFrom", "configMapRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "StatefulSet",
},
Path: []string{"spec", "template", "spec", "volumes", "configMap", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "StatefulSet",
},
Path: []string{"spec", "template", "spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "StatefulSet",
},
Path: []string{"spec", "template", "spec", "containers", "envFrom", "configMapRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "Job",
},
Path: []string{"spec", "template", "spec", "volumes", "configMap", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "Job",
},
Path: []string{"spec", "template", "spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "Job",
},
Path: []string{"spec", "template", "spec", "containers", "envFrom", "configMapRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "CronJob",
},
Path: []string{"spec", "jobTemplate", "spec", "template", "spec", "volumes", "configMap", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "CronJob",
},
Path: []string{"spec", "jobTemplate", "spec", "template", "spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "CronJob",
},
Path: []string{"spec", "jobTemplate", "spec", "template", "spec", "containers", "envFrom", "configMapRef", "name"},
CreateIfNotPresent: false,
},
},
},
{
referencedGVK: schema.GroupVersionKind{
Version: "v1",
Kind: "Secret",
},
pathConfigs: []PathConfig{
{
GroupVersionKind: &schema.GroupVersionKind{
Version: "v1",
Kind: "Pod",
},
Path: []string{"spec", "volumes", "secret", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Version: "v1",
Kind: "Pod",
},
Path: []string{"spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Version: "v1",
Kind: "Pod",
},
Path: []string{"spec", "containers", "envFrom", "secretRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "Deployment",
},
Path: []string{"spec", "template", "spec", "volumes", "secret", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "Deployment",
},
Path: []string{"spec", "template", "spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "Deployment",
},
Path: []string{"spec", "template", "spec", "containers", "envFrom", "secretRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "ReplicaSet",
},
Path: []string{"spec", "template", "spec", "volumes", "secret", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "ReplicaSet",
},
Path: []string{"spec", "template", "spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "ReplicaSet",
},
Path: []string{"spec", "template", "spec", "containers", "envFrom", "secretRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "DaemonSet",
},
Path: []string{"spec", "template", "spec", "volumes", "secret", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "DaemonSet",
},
Path: []string{"spec", "template", "spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "DaemonSet",
},
Path: []string{"spec", "template", "spec", "containers", "envFrom", "secretRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "StatefulSet",
},
Path: []string{"spec", "template", "spec", "volumes", "secret", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "StatefulSet",
},
Path: []string{"spec", "template", "spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "StatefulSet",
},
Path: []string{"spec", "template", "spec", "containers", "envFrom", "secretRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "Job",
},
Path: []string{"spec", "template", "spec", "volumes", "secret", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "Job",
},
Path: []string{"spec", "template", "spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "Job",
},
Path: []string{"spec", "template", "spec", "containers", "envFrom", "secretRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "CronJob",
},
Path: []string{"spec", "jobTemplate", "spec", "template", "spec", "volumes", "secret", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "CronJob",
},
Path: []string{"spec", "jobTemplate", "spec", "template", "spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
CreateIfNotPresent: false,
},
{
GroupVersionKind: &schema.GroupVersionKind{
Kind: "CronJob",
},
Path: []string{"spec", "jobTemplate", "spec", "template", "spec", "containers", "envFrom", "secretRef", "name"},
CreateIfNotPresent: false,
},
},
},
}

View File

@ -32,3 +32,24 @@ type PathConfig struct {
// Path to the field that will be munged.
Path []string
}
// referencePathConfig contains the configuration of a field that references
// the name of another resource whose GroupVersionKind is specified in referencedGVK.
// e.g. pod.spec.template.volumes.configMap.name references the name of a configmap
// Its corresponding referencePathConfig will look like:
//
// referencePathConfig{
// referencedGVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
// pathConfigs: []PathConfig{
// {
// GroupVersionKind: &schema.GroupVersionKind{Version: "v1", Kind: "Pod"},
// Path: []string{"spec", "volumes", "configMap", "name"},
// },
// }
type referencePathConfig struct {
// referencedGVK is the GroupVersionKind that is referenced by
// the PathConfig's GVK in the path of PathConfig.Path.
referencedGVK schema.GroupVersionKind
// PathConfig is the GVK that is referencing the referencedGVK object's name.
pathConfigs []PathConfig
}