Introduce envs field.

This commit is contained in:
Jeffrey Regan
2019-05-06 13:51:08 -07:00
parent a8c476f7c0
commit 529db0493b
15 changed files with 127 additions and 84 deletions

View File

@@ -105,9 +105,9 @@ secretGenerator:
- tls.key=secret/tls.key - tls.key=secret/tls.key
type: "kubernetes.io/tls" type: "kubernetes.io/tls"
- name: env_file_secret - name: env_file_secret
# env is a path to a file to read lines of key=val # paths to files with k=v pairs, one pair per line.
# you can only specify one env file per secret. envs:
env: env.txt - env.txt
type: Opaque type: Opaque
# generatorOptions modify behavior of all ConfigMap and Secret generators # generatorOptions modify behavior of all ConfigMap and Secret generators

View File

@@ -42,15 +42,14 @@ func (bf baseFactory) loadKvPairs(
if err != nil { if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf( return nil, errors.Wrap(err, fmt.Sprintf(
"plugins: %s", "plugins: %s",
args.EnvSource)) args.KVSources))
} }
all = append(all, pairs...) all = append(all, pairs...)
pairs, err = bf.keyValuesFromEnvFiles(args.EnvSources)
pairs, err = bf.keyValuesFromEnvFile(args.EnvSource)
if err != nil { if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf( return nil, errors.Wrap(err, fmt.Sprintf(
"env source file: %s", "env source files: %v",
args.EnvSource)) args.EnvSources))
} }
all = append(all, pairs...) all = append(all, pairs...)
@@ -136,13 +135,18 @@ func (bf baseFactory) keyValuesFromFileSources(sources []string) ([]kv.Pair, err
return kvs, nil return kvs, nil
} }
func (bf baseFactory) keyValuesFromEnvFile(path string) ([]kv.Pair, error) { func (bf baseFactory) keyValuesFromEnvFiles(paths []string) ([]kv.Pair, error) {
if path == "" { var kvs []kv.Pair
return nil, nil for _, path := range paths {
content, err := bf.ldr.Load(path)
if err != nil {
return nil, err
}
more, err := kv.KeyValuesFromLines(content)
if err != nil {
return nil, err
}
kvs = append(kvs, more...)
} }
content, err := bf.ldr.Load(path) return kvs, nil
if err != nil {
return nil, err
}
return kv.KeyValuesFromLines(content)
} }

View File

@@ -99,7 +99,7 @@ func TestConstructConfigMap(t *testing.T) {
GeneratorArgs: types.GeneratorArgs{ GeneratorArgs: types.GeneratorArgs{
Name: "envConfigMap", Name: "envConfigMap",
DataSources: types.DataSources{ DataSources: types.DataSources{
EnvSource: "configmap/app.env", EnvSources: []string{"configmap/app.env"},
}, },
}, },
}, },

View File

@@ -97,7 +97,7 @@ func TestConstructSecret(t *testing.T) {
GeneratorArgs: types.GeneratorArgs{ GeneratorArgs: types.GeneratorArgs{
Name: "envSecret", Name: "envSecret",
DataSources: types.DataSources{ DataSources: types.DataSources{
EnvSource: "secret/app.env", EnvSources: []string{"secret/app.env"},
}, },
}, },
}, },

View File

@@ -32,7 +32,6 @@ type Registry struct {
} }
const ( const (
PluginSymbol = "KustomizePlugin"
PluginRoot = "plugin" PluginRoot = "plugin"
pluginTypeGo = types.PluginType("go") pluginTypeGo = types.PluginType("go")
pluginTypeBuiltIn = types.PluginType("builtin") pluginTypeBuiltIn = types.PluginType("builtin")

View File

@@ -159,16 +159,13 @@ func (mf *kustomizationFile) Read() (*types.Kustomization, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
data = types.DealWithDeprecatedFields(data) data = types.FixKustomizationPreUnmarshalling(data)
var k types.Kustomization var k types.Kustomization
err = yaml.Unmarshal(data, &k) err = yaml.Unmarshal(data, &k)
if err != nil { if err != nil {
return nil, err return nil, err
} }
msgs := k.DealWithMissingFields() k.FixKustomizationPostUnmarshalling()
if len(msgs) > 0 {
log.Printf(strings.Join(msgs, "\n"))
}
err = mf.parseCommentedFields(data) err = mf.parseCommentedFields(data)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -81,7 +81,7 @@ func TestWriteAndRead(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Couldn't read kustomization file: %v\n", err) t.Fatalf("Couldn't read kustomization file: %v\n", err)
} }
kustomization.DealWithMissingFields() kustomization.FixKustomizationPostUnmarshalling()
if !reflect.DeepEqual(kustomization, content) { if !reflect.DeepEqual(kustomization, content) {
t.Fatal("Read kustomization is different from written kustomization") t.Fatal("Read kustomization is different from written kustomization")
} }

19
pkg/plugins/config.go Normal file
View File

@@ -0,0 +1,19 @@
/*
Copyright 2019 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 plugins
const pluginSymbol = "KustomizePlugin"

View File

@@ -22,7 +22,6 @@ import (
"plugin" "plugin"
"github.com/pkg/errors" "github.com/pkg/errors"
kplugin "sigs.k8s.io/kustomize/k8sdeps/kv/plugin"
"sigs.k8s.io/kustomize/pkg/ifc" "sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/resid" "sigs.k8s.io/kustomize/pkg/resid"
"sigs.k8s.io/kustomize/pkg/resmap" "sigs.k8s.io/kustomize/pkg/resmap"
@@ -122,11 +121,11 @@ func (l *Loader) loadGoPlugin(id resid.ResId) (c Configurable, err error) {
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "plugin %s fails to load", name) return nil, errors.Wrapf(err, "plugin %s fails to load", name)
} }
symbol, err := p.Lookup(kplugin.PluginSymbol) symbol, err := p.Lookup(pluginSymbol)
if err != nil { if err != nil {
return nil, errors.Wrapf( return nil, errors.Wrapf(
err, "plugin %s doesn't have symbol %s", err, "plugin %s doesn't have symbol %s",
name, kplugin.PluginSymbol) name, pluginSymbol)
} }
c, ok = symbol.(Configurable) c, ok = symbol.(Configurable)
if !ok { if !ok {

View File

@@ -153,7 +153,7 @@ func TestNewFromConfigMaps(t *testing.T) {
GeneratorArgs: types.GeneratorArgs{ GeneratorArgs: types.GeneratorArgs{
Name: "envConfigMap", Name: "envConfigMap",
DataSources: types.DataSources{ DataSources: types.DataSources{
EnvSource: "app.env", EnvSources: []string{"app.env"},
}, },
}, },
}, },

View File

@@ -158,21 +158,33 @@ patchesStrategicMerge:
- deployment/deployment.yaml - deployment/deployment.yaml
configMapGenerator: configMapGenerator:
- name: app-env - name: app-env
env: configmap/app.env env: configmap/db.env
envs:
- configmap/units.ini
- configmap/food.ini
- name: app-config - name: app-config
files: files:
- configmap/app-init.ini - nonsense=configmap/dummy.txt
images: images:
- name: nginx - name: nginx
newTag: 1.8.0`) newTag: 1.8.0`)
th.writeF("/app/overlay/configmap/app.env", ` th.writeF("/app/overlay/configmap/db.env", `
DB_USERNAME=admin DB_USERNAME=admin
DB_PASSWORD=somepw DB_PASSWORD=somepw
`) `)
th.writeF("/app/overlay/configmap/app-init.ini", ` th.writeF("/app/overlay/configmap/units.ini", `
FOO=bar LENGTH=kilometer
BAR=baz ENERGY=electronvolt
`)
th.writeF("/app/overlay/configmap/food.ini", `
FRUIT=banana
LEGUME=chickpea
`)
th.writeF("/app/overlay/configmap/dummy.txt",
`Lorem ipsum dolor sit amet, consectetur
adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.
`) `)
th.writeF("/app/overlay/deployment/deployment.yaml", ` th.writeF("/app/overlay/deployment/deployment.yaml", `
apiVersion: extensions/v1beta1 apiVersion: extensions/v1beta1
@@ -217,10 +229,8 @@ spec:
th.assertActualEqualsExpected(m, ` th.assertActualEqualsExpected(m, `
apiVersion: v1 apiVersion: v1
data: data:
app-init.ini: |2 nonsense: "Lorem ipsum dolor sit amet, consectetur\nadipiscing elit, sed do eiusmod
tempor\nincididunt ut labore et dolore magna aliqua. \n"
FOO=bar
BAR=baz
kind: ConfigMap kind: ConfigMap
metadata: metadata:
annotations: annotations:
@@ -229,12 +239,16 @@ metadata:
app: mungebot app: mungebot
org: kubernetes org: kubernetes
repo: test-infra repo: test-infra
name: test-infra-app-config-fd62mfc87h name: test-infra-app-config-f462h769f9
--- ---
apiVersion: v1 apiVersion: v1
data: data:
DB_PASSWORD: somepw DB_PASSWORD: somepw
DB_USERNAME: admin DB_USERNAME: admin
ENERGY: electronvolt
FRUIT: banana
LEGUME: chickpea
LENGTH: kilometer
kind: ConfigMap kind: ConfigMap
metadata: metadata:
annotations: annotations:
@@ -243,7 +257,7 @@ metadata:
app: mungebot app: mungebot
org: kubernetes org: kubernetes
repo: test-infra repo: test-infra
name: test-infra-app-env-bh449c299k name: test-infra-app-env-ffmd9b969m
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
@@ -303,7 +317,7 @@ spec:
valueFrom: valueFrom:
configMapKeyRef: configMapKeyRef:
key: somekey key: somekey
name: test-infra-app-env-bh449c299k name: test-infra-app-env-ffmd9b969m
- name: foo - name: foo
value: bar value: bar
image: nginx:1.8.0 image: nginx:1.8.0
@@ -314,7 +328,7 @@ spec:
- configMapRef: - configMapRef:
name: someConfigMap name: someConfigMap
- configMapRef: - configMapRef:
name: test-infra-app-env-bh449c299k name: test-infra-app-env-ffmd9b969m
image: busybox image: busybox
name: busybox name: busybox
volumeMounts: volumeMounts:
@@ -322,7 +336,7 @@ spec:
name: app-env name: app-env
volumes: volumes:
- configMap: - configMap:
name: test-infra-app-env-bh449c299k name: test-infra-app-env-ffmd9b969m
name: app-env name: app-env
`) `)
} }

View File

@@ -118,13 +118,14 @@ generators:
th.assertActualEqualsExpected(m, ` th.assertActualEqualsExpected(m, `
apiVersion: v1 apiVersion: v1
data: data:
DB_PASSWORD: aWxvdmV5b3U=
FRUIT: YXBwbGU= FRUIT: YXBwbGU=
ROUTER_PASSWORD: YWRtaW4= ROUTER_PASSWORD: YWRtaW4=
VEGETABLE: Y2Fycm90 VEGETABLE: Y2Fycm90
longsecret.txt: CkxvcmVtIGlwc3VtIGRvbG9yIHNpdCBhbWV0LApjb25zZWN0ZXR1ciBhZGlwaXNjaW5nIGVsaXQsCnNlZCBkbyBlaXVzbW9kIHRlbXBvciBpbmNpZGlkdW50CnV0IGxhYm9yZSBldCBkb2xvcmUgbWFnbmEgYWxpcXVhLgo= longsecret.txt: CkxvcmVtIGlwc3VtIGRvbG9yIHNpdCBhbWV0LApjb25zZWN0ZXR1ciBhZGlwaXNjaW5nIGVsaXQsCnNlZCBkbyBlaXVzbW9kIHRlbXBvciBpbmNpZGlkdW50CnV0IGxhYm9yZSBldCBkb2xvcmUgbWFnbmEgYWxpcXVhLgo=
kind: Secret kind: Secret
metadata: metadata:
name: -2kt2h55789 name: -ktm999dkcc
type: Opaque type: Opaque
`) `)
} }

View File

@@ -57,12 +57,13 @@ func NewKustTarget(
if err != nil { if err != nil {
return nil, err return nil, err
} }
content = types.DealWithDeprecatedFields(content) content = types.FixKustomizationPreUnmarshalling(content)
var k types.Kustomization var k types.Kustomization
err = unmarshal(content, &k) err = unmarshal(content, &k)
if err != nil { if err != nil {
return nil, err return nil, err
} }
k.FixKustomizationPostUnmarshalling()
errs := k.EnforceFields() errs := k.EnforceFields()
if len(errs) > 0 { if len(errs) > 0 {
return nil, fmt.Errorf( return nil, fmt.Errorf(

View File

@@ -142,18 +142,32 @@ type Kustomization struct {
Inventory *Inventory `json:"inventory,omitempty" yaml:"inventory:omitempty"` Inventory *Inventory `json:"inventory,omitempty" yaml:"inventory:omitempty"`
} }
// DealWithMissingFields fills the missing fields // FixKustomizationPostUnmarshalling fixes things
func (k *Kustomization) DealWithMissingFields() []string { // like empty fields that should not be empty, or
var msgs []string // moving content of deprecated fields to newer
// fields.
func (k *Kustomization) FixKustomizationPostUnmarshalling() {
if k.APIVersion == "" { if k.APIVersion == "" {
k.APIVersion = KustomizationVersion k.APIVersion = KustomizationVersion
msgs = append(msgs, "Fixed the missing field by adding apiVersion: "+KustomizationVersion)
} }
if k.Kind == "" { if k.Kind == "" {
k.Kind = KustomizationKind k.Kind = KustomizationKind
msgs = append(msgs, "Fixed the missing field by adding kind: "+KustomizationKind)
} }
return msgs // The EnvSource field is deprecated in favor of the list.
for i, g := range k.ConfigMapGenerator {
if g.EnvSource != "" {
k.ConfigMapGenerator[i].EnvSources =
append(g.EnvSources, g.EnvSource)
k.ConfigMapGenerator[i].EnvSource = ""
}
}
for i, g := range k.SecretGenerator {
if g.EnvSource != "" {
k.SecretGenerator[i].EnvSources =
append(g.EnvSources, g.EnvSource)
k.SecretGenerator[i].EnvSource = ""
}
}
} }
func (k *Kustomization) EnforceFields() []string { func (k *Kustomization) EnforceFields() []string {
@@ -167,9 +181,10 @@ func (k *Kustomization) EnforceFields() []string {
return errs return errs
} }
// DealWithDeprecatedFields should be called immediately after // FixKustomizationPreUnmarshalling modies the raw data
// loading from storage. // before marshalling - e.g. changes old field names to
func DealWithDeprecatedFields(data []byte) []byte { // new field names.
func FixKustomizationPreUnmarshalling(data []byte) []byte {
deprecateFieldsMap := map[string]string{ deprecateFieldsMap := map[string]string{
"patches:": "patchesStrategicMerge:", "patches:": "patchesStrategicMerge:",
"imageTags:": "images:", "imageTags:": "images:",
@@ -246,24 +261,31 @@ type SecretArgs struct {
// DataSources contains some generic sources for configmaps. // DataSources contains some generic sources for configmaps.
type DataSources struct { type DataSources struct {
// LiteralSources is a list of literal sources. // LiteralSources is a list of literal
// Each literal source should be a key and literal value, // pair sources. Each literal source should
// e.g. `somekey=somevalue` // be a key and literal value, e.g. `key=value`
// It will be similar to kubectl create configmap|secret --from-literal
LiteralSources []string `json:"literals,omitempty" yaml:"literals,omitempty"` LiteralSources []string `json:"literals,omitempty" yaml:"literals,omitempty"`
// FileSources is a list of file sources. // FileSources is a list of file "sources" to
// Each file source can be specified using its file path, in which case file // use in creating a list of key, value pairs.
// basename will be used as configmap key, or optionally with a key and file // A source takes the form: [{key}=]{path}
// path, in which case the given key will be used. // If the "key=" part is missing, the key is the
// Specifying a directory will iterate each named file in the directory // path's basename. If they "key=" part is present,
// whose basename is a valid configmap key. // it becomes the key (replacing the basename).
// It will be similar to kubectl create configmap|secret --from-file // In either case, the value is the file contents.
// Specifying a directory will iterate each named
// file in the directory whose basename is a
// valid configmap key.
FileSources []string `json:"files,omitempty" yaml:"files,omitempty"` FileSources []string `json:"files,omitempty" yaml:"files,omitempty"`
// EnvSource format should be a path to a file to read lines of key=val // EnvSources is a list of file paths.
// pairs to create a configmap. // The contents of each file should be one
// i.e. a Docker .env file or a .ini file. // key=value pair per line, e.g. a Docker
// or npm ".env" file or a ".ini" file
// (wikipedia.org/wiki/INI_file)
EnvSources []string `json:"envs,omitempty" yaml:"envs,omitempty"`
// Deprecated. Use EnvSources instead.
EnvSource string `json:"env,omitempty" yaml:"env,omitempty"` EnvSource string `json:"env,omitempty" yaml:"env,omitempty"`
} }

View File

@@ -3,8 +3,6 @@
package main package main
import ( import (
"fmt"
"sigs.k8s.io/kustomize/pkg/ifc" "sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/resmap" "sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/types" "sigs.k8s.io/kustomize/pkg/types"
@@ -23,26 +21,16 @@ func (p *plugin) Config(
ldr ifc.Loader, rf *resmap.Factory, k ifc.Kunstructured) error { ldr ifc.Loader, rf *resmap.Factory, k ifc.Kunstructured) error {
p.ldr = ldr p.ldr = ldr
p.rf = rf p.rf = rf
var err error var err error
// TODO: Should validate this. // TODO: validate behavior values.
p.args.Behavior, err = k.GetFieldValue("behavior") p.args.Behavior, err = k.GetFieldValue("behavior")
if err != nil { if err != nil {
return err return err
} }
p.args.EnvSources, err = k.GetStringSlice("envFiles")
envFiles, err := k.GetStringSlice("envFiles")
if err != nil { if err != nil {
return err return err
} }
if len(envFiles) > 2 {
// TODO: refactor to allow this
return fmt.Errorf("cannot yet accept more than one envFile")
}
if len(envFiles) > 0 {
p.args.EnvSource = envFiles[0]
}
p.args.FileSources, err = k.GetStringSlice("valueFiles") p.args.FileSources, err = k.GetStringSlice("valueFiles")
if err != nil { if err != nil {
return err return err
@@ -51,7 +39,6 @@ func (p *plugin) Config(
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }