Merge pull request #240 from apelisse/apelisse-droot-seans3

kinflate: Manifest type improvements
This commit is contained in:
k8s-ci-robot 2018-02-05 10:28:11 -08:00 committed by GitHub
commit 1f4c1efbdf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 390 additions and 389 deletions

View File

@ -30,7 +30,7 @@ func main() {
cmd.AddCommand(commands.NewCmdInflate(os.Stdout, os.Stderr))
cmd.AddCommand(commands.NewCmdAddResource(os.Stdout, os.Stderr, fs.MakeRealFS()))
cmd.AddCommand(commands.NewCmdInit(os.Stdout, os.Stderr, fs.MakeRealFS()))
cmd.AddCommand(commands.NewCmdAddConfigMap(os.Stderr))
cmd.AddCommand(commands.NewCmdAddConfigMap(os.Stderr, fs.MakeRealFS()))
cmd.AddCommand(commands.NewCmdAddSecret(os.Stderr))
err := cmd.Execute()

View File

@ -100,11 +100,17 @@ type Manifest struct {
// and Overlays fields.
Configmaps []ConfigMap `json:"configmaps,omitempty" yaml:"configmaps,omitempty"`
// List of secrets to generate from secret sources.
// List of generic secrets to generate from secret sources.
// Base/overlay concept doesn't apply to this field.
// If a secret want to have a base and an overlay, it should go to Bases and
// Overlays fields.
Secrets []Secret `json:"secrets,omitempty" yaml:"secrets,omitempty"`
GenericSecrets []GenericSecret `json:"genericSecrets,omitempty" yaml:"genericSecrets,omitempty"`
// List of TLS secrets to generate from secret sources.
// Base/overlay concept doesn't apply to this field.
// If a secret want to have a base and an overlay, it should go to Bases and
// Overlays fields.
TLSSecrets []TLSSecret `json:"tlsSecrets,omitempty" yaml:"tlsSecrets,omitempty"`
// Whether prune resources not defined in Kube-manifest.yaml, similar to
// `kubectl apply --prune` behavior.
@ -122,41 +128,43 @@ type Manifest struct {
// ConfigMap contains the metadata of how to generate a configmap.
type ConfigMap struct {
// The type of the configmap. e.g. `env`, `file`, `literal`.
Type string `json:"type,omitempty" yaml:"type,omitempty"`
// Name prefix of the configmap.
// The full name should be Manifest.NamePrefix + Configmap.NamePrefix +
// Name of the configmap.
// The full name should be Manifest.NamePrefix + Configmap.Name +
// hash(content of configmap).
NamePrefix string `json:"namePrefix,omitempty" yaml:"namePrefix,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
// Generic source for configmap, it could of one of `env`, `file`, `literal`
Generic `json:",inline,omitempty" yaml:",inline,omitempty"`
// DataSources for configmap.
DataSources `json:",inline,omitempty" yaml:",inline,omitempty"`
}
// Secret contains the metadata of how to generate a secret.
// Only one of source or tls can be set.
type Secret struct {
// The type of the secret. e.g. `generic` and `tls`.
Type string `json:"type,omitempty" yaml:"type,omitempty"`
// Name prefix of the secret.
// The full name should be Manifest.NamePrefix + Secret.NamePrefix +
// GenericSecret contains the metadata of how to generate a generic secret.
type GenericSecret struct {
// Name of the secret.
// The full name should be Manifest.NamePrefix + GenericSecret.Name +
// hash(content of secret).
NamePrefix string `json:"namePrefix,omitempty" yaml:"namePrefix,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
// Generic source for secret, it could of one of `env`, `file`, `literal`
Generic `json:",inline,omitempty" yaml:",inline,omitempty"`
// TLS secret.
TLS *TLS `json:"tls,omitempty" yaml:"tls,omitempty"`
// TODO: support more secret types, e.g. DockerRegistry
// DataSources for secret.
DataSources `json:",inline,omitempty" yaml:",inline,omitempty"`
}
// Generic contains some generic sources for configmap or secret.
// TLSSecret contains the metadata of how to generate a TLS secret.
type TLSSecret struct {
// Name of the secret
// The full name should be Manifest.NamePrefix + TLSSecret.Name +
// hash(content of secret).
Name string `json:"name,omitempty" yaml:"name,omitempty"`
// Path to PEM encoded public key certificate.
CertFile string `json:"certFile,omitempty" yaml:"certFile,omitempty"`
// Path to private key associated with given certificate.
KeyFile string `json:"keyFile,omitempty" yaml:"keyFile,omitempty"`
}
// DataSources contains some generic sources for configmap or secret.
// Only one field can be set.
type Generic struct {
type DataSources struct {
// LiteralSources is a list of literal sources.
// Each literal source should be a key and literal value,
// e.g. `somekey=somevalue`
@ -177,12 +185,3 @@ type Generic struct {
// i.e. a Docker .env file or a .ini file.
EnvSource string `json:"env,omitempty" yaml:"env,omitempty"`
}
// TLS contains cert and key paths.
type TLS struct {
// Path to PEM encoded public key certificate.
CertFile string `json:"certFile,omitempty" yaml:"certFile,omitempty"`
// Path to private key associated with given certificate.
KeyFile string `json:"keyFile,omitempty" yaml:"keyFile,omitempty"`
}

View File

@ -26,8 +26,8 @@ func adjustPathsForManifest(m *manifest.Manifest, pathToDir []string) {
m.Resources = adjustPaths(m.Resources, pathToDir)
m.Patches = adjustPaths(m.Patches, pathToDir)
m.Configmaps = adjustPathForConfigMaps(m.Configmaps, pathToDir)
m.Secrets = adjustPathForSecrets(m.Secrets, pathToDir)
m.GenericSecrets = adjustPathForGenericSecrets(m.GenericSecrets, pathToDir)
m.TLSSecrets = adjustPathForTLSSecrets(m.TLSSecrets, pathToDir)
}
func adjustPathForConfigMaps(cms []manifest.ConfigMap, prefix []string) []manifest.ConfigMap {
@ -44,7 +44,7 @@ func adjustPathForConfigMaps(cms []manifest.ConfigMap, prefix []string) []manife
return cms
}
func adjustPathForSecrets(secrets []manifest.Secret, prefix []string) []manifest.Secret {
func adjustPathForGenericSecrets(secrets []manifest.GenericSecret, prefix []string) []manifest.GenericSecret {
for i, secret := range secrets {
if len(secret.FileSources) > 0 {
for j, fileSource := range secret.FileSources {
@ -54,10 +54,14 @@ func adjustPathForSecrets(secrets []manifest.Secret, prefix []string) []manifest
if len(secret.EnvSource) > 0 {
secrets[i].EnvSource = adjustPath(secret.EnvSource, prefix)
}
if secret.TLS != nil {
secrets[i].TLS.CertFile = adjustPath(secret.TLS.CertFile, prefix)
secrets[i].TLS.KeyFile = adjustPath(secret.TLS.KeyFile, prefix)
}
}
return secrets
}
func adjustPathForTLSSecrets(secrets []manifest.TLSSecret, prefix []string) []manifest.TLSSecret {
for i, secret := range secrets {
secrets[i].CertFile = adjustPath(secret.CertFile, prefix)
secrets[i].KeyFile = adjustPath(secret.KeyFile, prefix)
}
return secrets
}

View File

@ -17,41 +17,15 @@ limitations under the License.
package commands
import (
"fmt"
"io"
"k8s.io/kubectl/pkg/kinflate/util/fs"
"github.com/spf13/cobra"
)
type addConfigMap struct {
// Name of configMap (required)
Name string
// FileSources to derive the configMap from (optional)
FileSources []string
// LiteralSources to derive the configMap from (optional)
LiteralSources []string
// EnvFileSource to derive the configMap from (optional)
EnvFileSource string
}
// validate validates required fields are set to support structured generation.
func (a *addConfigMap) Validate(args []string) error {
if len(args) != 1 {
return fmt.Errorf("name must be specified once")
}
a.Name = args[0]
if len(a.EnvFileSource) == 0 && len(a.FileSources) == 0 && len(a.LiteralSources) == 0 {
return fmt.Errorf("at least from-env-file, or from-file or from-literal must be set")
}
if len(a.EnvFileSource) > 0 && (len(a.FileSources) > 0 || len(a.LiteralSources) > 0) {
return fmt.Errorf("from-env-file cannot be combined with from-file or from-literal")
}
// TODO: Should we check if the path exists? if it's valid, if it's within the same (sub-)directory?
return nil
}
func NewCmdAddConfigMap(errOut io.Writer) *cobra.Command {
var config addConfigMap
func NewCmdAddConfigMap(errOut io.Writer, fsys fs.FileSystem) *cobra.Command {
var config dataConfig
cmd := &cobra.Command{
Use: "configmap NAME [--from-file=[key=]source] [--from-literal=key1=value1]",
Short: "Adds a configmap to your manifest file.",

View File

@ -18,72 +18,12 @@ package commands
import (
"testing"
"k8s.io/kubectl/pkg/kinflate/util/fs"
)
func TestNewAddConfigMapIsNotNil(t *testing.T) {
if NewCmdAddConfigMap(nil) == nil {
if NewCmdAddConfigMap(nil, fs.MakeFakeFS()) == nil {
t.Fatal("NewCmdAddConfigMap shouldn't be nil")
}
}
func TestAddConfigValidation_NoName(t *testing.T) {
config := addConfigMap{}
if config.Validate([]string{}) == nil {
t.Fatal("Validation should fail if no name is specified")
}
}
func TestAddConfigValidation_MoreThanOneName(t *testing.T) {
config := addConfigMap{}
if config.Validate([]string{"name", "othername"}) == nil {
t.Fatal("Validation should fail if more than one name is specified")
}
}
func TestAddConfigValidation_Flags(t *testing.T) {
tests := []struct {
name string
config addConfigMap
shouldFail bool
}{
{
name: "env-file-source and literal are both set",
config: addConfigMap{
LiteralSources: []string{"one", "two"},
EnvFileSource: "three",
},
shouldFail: true,
},
{
name: "env-file-source and from-file are both set",
config: addConfigMap{
FileSources: []string{"one", "two"},
EnvFileSource: "three",
},
shouldFail: true,
},
{
name: "we don't have any option set",
config: addConfigMap{},
shouldFail: true,
},
{
name: "we have from-file and literal ",
config: addConfigMap{
LiteralSources: []string{"one", "two"},
FileSources: []string{"three", "four"},
},
shouldFail: false,
},
}
for _, test := range tests {
if test.config.Validate([]string{"name"}) == nil && test.shouldFail {
t.Fatalf("Validation should fail if %s", test.name)
} else if test.config.Validate([]string{"name"}) != nil && !test.shouldFail {
t.Fatalf("Validation should succeed if %s", test.name)
}
}
}

View File

@ -0,0 +1,49 @@
/*
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 commands
import (
"fmt"
)
// dataConfig encapsulates the options for add configmap/Secret commands.
type dataConfig struct {
// Name of configMap/Secret (required)
Name string
// FileSources to derive the configMap/Secret from (optional)
FileSources []string
// LiteralSources to derive the configMap/Secret from (optional)
LiteralSources []string
// EnvFileSource to derive the configMap/Secret from (optional)
EnvFileSource string
}
// Validate validates required fields are set to support structured generation.
func (a *dataConfig) Validate(args []string) error {
if len(args) != 1 {
return fmt.Errorf("name must be specified once")
}
a.Name = args[0]
if len(a.EnvFileSource) == 0 && len(a.FileSources) == 0 && len(a.LiteralSources) == 0 {
return fmt.Errorf("at least from-env-file, or from-file or from-literal must be set")
}
if len(a.EnvFileSource) > 0 && (len(a.FileSources) > 0 || len(a.LiteralSources) > 0) {
return fmt.Errorf("from-env-file cannot be combined with from-file or from-literal")
}
// TODO: Should we check if the path exists? if it's valid, if it's within the same (sub-)directory?
return nil
}

View File

@ -0,0 +1,83 @@
/*
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 commands
import (
"testing"
)
func TestDataConfigValidation_NoName(t *testing.T) {
config := dataConfig{}
if config.Validate([]string{}) == nil {
t.Fatal("Validation should fail if no name is specified")
}
}
func TestDataConfigValidation_MoreThanOneName(t *testing.T) {
config := dataConfig{}
if config.Validate([]string{"name", "othername"}) == nil {
t.Fatal("Validation should fail if more than one name is specified")
}
}
func TestDataConfigValidation_Flags(t *testing.T) {
tests := []struct {
name string
config dataConfig
shouldFail bool
}{
{
name: "env-file-source and literal are both set",
config: dataConfig{
LiteralSources: []string{"one", "two"},
EnvFileSource: "three",
},
shouldFail: true,
},
{
name: "env-file-source and from-file are both set",
config: dataConfig{
FileSources: []string{"one", "two"},
EnvFileSource: "three",
},
shouldFail: true,
},
{
name: "we don't have any option set",
config: dataConfig{},
shouldFail: true,
},
{
name: "we have from-file and literal ",
config: dataConfig{
LiteralSources: []string{"one", "two"},
FileSources: []string{"three", "four"},
},
shouldFail: false,
},
}
for _, test := range tests {
if test.config.Validate([]string{"name"}) == nil && test.shouldFail {
t.Fatalf("Validation should fail if %s", test.name)
} else if test.config.Validate([]string{"name"}) != nil && !test.shouldFail {
t.Fatalf("Validation should succeed if %s", test.name)
}
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2017 The Kubernetes Authors.
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.
@ -23,35 +23,8 @@ import (
"github.com/spf13/cobra"
)
type addGenericSecret struct {
// Name of secret (required)
Name string
// FileSources to derive the secret from (optional)
FileSources []string
// LiteralSources to derive the secret from (optional)
LiteralSources []string
// EnvFileSource to derive the secret from (optional)
EnvFileSource string
}
// Validate validates required fields are set to support structured generation.
func (a *addGenericSecret) Validate(args []string) error {
if len(args) != 1 {
return fmt.Errorf("name must be specified once")
}
a.Name = args[0]
if len(a.EnvFileSource) == 0 && len(a.FileSources) == 0 && len(a.LiteralSources) == 0 {
return fmt.Errorf("at least from-env-file, or from-file or from-literal must be set")
}
if len(a.EnvFileSource) > 0 && (len(a.FileSources) > 0 || len(a.LiteralSources) > 0) {
return fmt.Errorf("from-env-file cannot be combined with from-file or from-literal")
}
// TODO: Should we check if the path exists? if it's valid, if it's within the same (sub-)directory?
return nil
}
func newCmdAddSecretGeneric(errOut io.Writer) *cobra.Command {
var config addGenericSecret
var config dataConfig
cmd := &cobra.Command{
Use: "generic NAME [--type=string] [--from-file=[key=]source] [--from-literal=key1=value1]",
Short: "Adds a secret from a local file, directory or literal value.",

View File

@ -1,5 +1,5 @@
/*
Copyright 2017 The Kubernetes Authors.
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.
@ -25,125 +25,3 @@ func TestNewAddSecretIsNotNil(t *testing.T) {
t.Fatal("NewCmdAddSecret shouldn't be nil")
}
}
func TestAddGenericSecretValidation_NoName(t *testing.T) {
config := addGenericSecret{}
if config.Validate([]string{}) == nil {
t.Fatal("Validation should fail if no name is specified")
}
}
func TestAddGenericSecretValidation_MoreThanOneName(t *testing.T) {
config := addGenericSecret{}
if config.Validate([]string{"name", "othername"}) == nil {
t.Fatal("Validation should fail if more than one name is specified")
}
}
func TestAddGenericSecretValidation_Flags(t *testing.T) {
tests := []struct {
name string
config addGenericSecret
shouldFail bool
}{
{
name: "env-file-source and literal are both set",
config: addGenericSecret{
LiteralSources: []string{"one", "two"},
EnvFileSource: "three",
},
shouldFail: true,
},
{
name: "env-file-source and from-file are both set",
config: addGenericSecret{
FileSources: []string{"one", "two"},
EnvFileSource: "three",
},
shouldFail: true,
},
{
name: "we don't have any option set",
config: addGenericSecret{},
shouldFail: true,
},
{
name: "we have from-file and literal ",
config: addGenericSecret{
LiteralSources: []string{"one", "two"},
FileSources: []string{"three", "four"},
},
shouldFail: false,
},
}
for _, test := range tests {
if test.config.Validate([]string{"name"}) == nil && test.shouldFail {
t.Fatalf("Validation should fail if %s", test.name)
} else if test.config.Validate([]string{"name"}) != nil && !test.shouldFail {
t.Fatalf("Validation should succeed if %s", test.name)
}
}
}
func TestAddTLSSecretValidation_NoName(t *testing.T) {
config := addTLSSecret{}
if config.Validate([]string{}) == nil {
t.Fatal("Validation should fail if no name is specified")
}
}
func TestAddTLSSecretValidation_MoreThanOneName(t *testing.T) {
config := addTLSSecret{}
if config.Validate([]string{"name", "othername"}) == nil {
t.Fatal("Validation should fail if more than one name is specified")
}
}
func TestAddTLSSecretValidation_Flags(t *testing.T) {
tests := []struct {
name string
config addTLSSecret
shouldFail bool
}{
{
name: "cert and key are set",
config: addTLSSecret{
Cert: "cert",
Key: "key",
},
shouldFail: false,
},
{
name: "cert is set, but not key",
config: addTLSSecret{
Cert: "cert",
},
shouldFail: true,
},
{
name: "key is set, but not cert",
config: addTLSSecret{
Key: "key",
},
shouldFail: true,
},
{
name: "neither key nor cert is set",
config: addTLSSecret{},
shouldFail: true,
},
}
for _, test := range tests {
if test.config.Validate([]string{"name"}) == nil && test.shouldFail {
t.Fatalf("Validation should fail if %s", test.name)
} else if test.config.Validate([]string{"name"}) != nil && !test.shouldFail {
t.Fatalf("Validation should succeed if %s", test.name)
}
}
}

View File

@ -45,18 +45,31 @@ func MakeConfigmapAndGenerateName(cm manifest.ConfigMap) (*unstructured.Unstruct
return unstructuredCM, nameWithHash, err
}
// MakeSecretAndGenerateName makes a secret and returns the secret and the name appended with a hash.
func MakeSecretAndGenerateName(secret manifest.Secret) (*unstructured.Unstructured, string, error) {
corev1Secret, err := makeSecret(secret)
// MakeGenericSecretAndGenerateName makes a generic secret and returns the secret and the name appended with a hash.
func MakeGenericSecretAndGenerateName(secret manifest.GenericSecret) (*unstructured.Unstructured, string, error) {
corev1Secret, err := makeGenericSecret(secret)
if err != nil {
return nil, "", err
}
h, err := hash.SecretHash(corev1Secret)
return makeSecretAndGenerateName(corev1Secret, secret.Name)
}
// MakeTLSSecretAndGenerateName makes a generic secret and returns the secret and the name appended with a hash.
func MakeTLSSecretAndGenerateName(secret manifest.TLSSecret) (*unstructured.Unstructured, string, error) {
corev1Secret, err := makeTlsSecret(secret)
if err != nil {
return nil, "", err
}
nameWithHash := fmt.Sprintf("%s-%s", corev1Secret.GetName(), h)
unstructuredCM, err := objectToUnstructured(corev1Secret)
return makeSecretAndGenerateName(corev1Secret, secret.Name)
}
func makeSecretAndGenerateName(secret *corev1.Secret, name string) (*unstructured.Unstructured, string, error) {
h, err := hash.SecretHash(secret)
if err != nil {
return nil, "", err
}
nameWithHash := fmt.Sprintf("%s-%s", name, h)
unstructuredCM, err := objectToUnstructured(secret)
return unstructuredCM, nameWithHash, err
}
@ -74,55 +87,76 @@ func makeConfigMap(cm manifest.ConfigMap) (*corev1.ConfigMap, error) {
corev1cm := &corev1.ConfigMap{}
corev1cm.APIVersion = "v1"
corev1cm.Kind = "ConfigMap"
corev1cm.Name = cm.NamePrefix
corev1cm.Name = cm.Name
corev1cm.Data = map[string]string{}
var err error
switch cm.Type {
case "env":
err = cutil.HandleConfigMapFromEnvFileSource(corev1cm, cm.EnvSource)
case "file":
err = cutil.HandleConfigMapFromFileSources(corev1cm, cm.FileSources)
case "literal":
err = cutil.HandleConfigMapFromLiteralSources(corev1cm, cm.LiteralSources)
default:
err = fmt.Errorf("unknown type of configmap: %v", cm.Type)
if cm.EnvSource != "" {
if err := cutil.HandleConfigMapFromEnvFileSource(corev1cm, cm.EnvSource); err != nil {
return nil, err
}
}
return corev1cm, err
if cm.FileSources != nil {
if err := cutil.HandleConfigMapFromFileSources(corev1cm, cm.FileSources); err != nil {
return nil, err
}
}
if cm.LiteralSources != nil {
if err := cutil.HandleConfigMapFromLiteralSources(corev1cm, cm.LiteralSources); err != nil {
return nil, err
}
}
return corev1cm, nil
}
func makeSecret(secret manifest.Secret) (*corev1.Secret, error) {
func makeGenericSecret(secret manifest.GenericSecret) (*corev1.Secret, error) {
corev1secret := &corev1.Secret{}
corev1secret.APIVersion = "v1"
corev1secret.Kind = "Secret"
corev1secret.Name = secret.NamePrefix
corev1secret.Name = secret.Name
corev1secret.Type = corev1.SecretTypeOpaque
corev1secret.Data = map[string][]byte{}
var err error
switch secret.Type {
case "tls":
if err = validateTLS(secret.TLS.CertFile, secret.TLS.KeyFile); err != nil {
if secret.EnvSource != "" {
if err := cutil.HandleFromEnvFileSource(corev1secret, secret.EnvSource); err != nil {
return nil, err
}
tlsCrt, err := ioutil.ReadFile(secret.TLS.CertFile)
if err != nil {
return nil, err
}
tlsKey, err := ioutil.ReadFile(secret.TLS.KeyFile)
if err != nil {
return nil, err
}
corev1secret.Type = corev1.SecretTypeTLS
corev1secret.Data[corev1.TLSCertKey] = []byte(tlsCrt)
corev1secret.Data[corev1.TLSPrivateKeyKey] = []byte(tlsKey)
case "env":
err = cutil.HandleFromEnvFileSource(corev1secret, secret.EnvSource)
case "file":
err = cutil.HandleFromFileSources(corev1secret, secret.FileSources)
case "literal":
err = cutil.HandleFromLiteralSources(corev1secret, secret.LiteralSources)
default:
err = fmt.Errorf("unknown type of secret: %v", secret.Type)
}
if secret.FileSources != nil {
if err := cutil.HandleFromFileSources(corev1secret, secret.FileSources); err != nil {
return nil, err
}
}
if secret.LiteralSources != nil {
if err := cutil.HandleFromLiteralSources(corev1secret, secret.LiteralSources); err != nil {
return nil, err
}
}
return corev1secret, nil
}
func makeTlsSecret(secret manifest.TLSSecret) (*corev1.Secret, error) {
corev1secret := &corev1.Secret{}
corev1secret.APIVersion = "v1"
corev1secret.Kind = "Secret"
corev1secret.Name = secret.Name
corev1secret.Type = corev1.SecretTypeTLS
corev1secret.Data = map[string][]byte{}
if err := validateTLS(secret.CertFile, secret.KeyFile); err != nil {
return nil, err
}
tlsCrt, err := ioutil.ReadFile(secret.CertFile)
if err != nil {
return nil, err
}
tlsKey, err := ioutil.ReadFile(secret.KeyFile)
if err != nil {
return nil, err
}
corev1secret.Data[corev1.TLSCertKey] = []byte(tlsCrt)
corev1secret.Data[corev1.TLSPrivateKeyKey] = []byte(tlsKey)
return corev1secret, err
}

View File

@ -212,9 +212,8 @@ func TestConstructConfigMap(t *testing.T) {
{
description: "construct config map from env",
input: manifest.ConfigMap{
Type: "env",
NamePrefix: "envConfigMap",
Generic: manifest.Generic{
Name: "envConfigMap",
DataSources: manifest.DataSources{
EnvSource: "../examples/simple/instances/exampleinstance/configmap/app.env",
},
},
@ -223,9 +222,8 @@ func TestConstructConfigMap(t *testing.T) {
{
description: "construct config map from file",
input: manifest.ConfigMap{
Type: "file",
NamePrefix: "fileConfigMap",
Generic: manifest.Generic{
Name: "fileConfigMap",
DataSources: manifest.DataSources{
FileSources: []string{"../examples/simple/instances/exampleinstance/configmap/app-init.ini"},
},
},
@ -234,9 +232,8 @@ func TestConstructConfigMap(t *testing.T) {
{
description: "construct config map from literal",
input: manifest.ConfigMap{
Type: "literal",
NamePrefix: "literalConfigMap",
Generic: manifest.Generic{
Name: "literalConfigMap",
DataSources: manifest.DataSources{
LiteralSources: []string{"a=x", "b=y"},
},
},
@ -246,6 +243,36 @@ func TestConstructConfigMap(t *testing.T) {
for _, tc := range testCases {
cm, err := makeConfigMap(tc.input)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(*cm, *tc.expected) {
t.Fatalf("in testcase: %q updated:\n%#v\ndoesn't match expected:\n%#v\n", tc.description, *cm, tc.expected)
}
}
}
func TestConstructTLSSecret(t *testing.T) {
type testCase struct {
description string
input manifest.TLSSecret
expected *corev1.Secret
}
testCases := []testCase{
{
description: "construct secret from tls",
input: manifest.TLSSecret{
Name: "tlsSecret",
CertFile: "../examples/simple/instances/exampleinstance/secret/tls.cert",
KeyFile: "../examples/simple/instances/exampleinstance/secret/tls.key",
},
expected: makeTLSSecret("tlsSecret"),
},
}
for _, tc := range testCases {
cm, err := makeTlsSecret(tc.input)
if err != nil {
t.Fatalf("unepxected error: %v", err)
}
@ -255,32 +282,19 @@ func TestConstructConfigMap(t *testing.T) {
}
}
func TestConstructSecret(t *testing.T) {
func TestConstructGenericSecret(t *testing.T) {
type testCase struct {
description string
input manifest.Secret
input manifest.GenericSecret
expected *corev1.Secret
}
testCases := []testCase{
{
description: "construct secret from tls",
input: manifest.Secret{
Type: "tls",
NamePrefix: "tlsSecret",
TLS: &manifest.TLS{
CertFile: "../examples/simple/instances/exampleinstance/secret/tls.cert",
KeyFile: "../examples/simple/instances/exampleinstance/secret/tls.key",
},
},
expected: makeTLSSecret("tlsSecret"),
},
{
description: "construct secret from env",
input: manifest.Secret{
Type: "env",
NamePrefix: "envSecret",
Generic: manifest.Generic{
input: manifest.GenericSecret{
Name: "envSecret",
DataSources: manifest.DataSources{
EnvSource: "../examples/simple/instances/exampleinstance/configmap/app.env",
},
},
@ -288,10 +302,9 @@ func TestConstructSecret(t *testing.T) {
},
{
description: "construct secret from file",
input: manifest.Secret{
Type: "file",
NamePrefix: "fileSecret",
Generic: manifest.Generic{
input: manifest.GenericSecret{
Name: "fileSecret",
DataSources: manifest.DataSources{
FileSources: []string{"../examples/simple/instances/exampleinstance/configmap/app-init.ini"},
},
},
@ -299,10 +312,9 @@ func TestConstructSecret(t *testing.T) {
},
{
description: "construct secret from literal",
input: manifest.Secret{
Type: "literal",
NamePrefix: "literalSecret",
Generic: manifest.Generic{
input: manifest.GenericSecret{
Name: "literalSecret",
DataSources: manifest.DataSources{
LiteralSources: []string{"a=x", "b=y"},
},
},
@ -311,9 +323,9 @@ func TestConstructSecret(t *testing.T) {
}
for _, tc := range testCases {
cm, err := makeSecret(tc.input)
cm, err := makeGenericSecret(tc.input)
if err != nil {
t.Fatalf("unepxected error: %v", err)
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(*cm, *tc.expected) {
t.Fatalf("in testcase: %q updated:\n%#v\ndoesn't match expected:\n%#v\n", tc.description, *cm, tc.expected)

View File

@ -22,19 +22,15 @@ patches:
- deployment/deployment.yaml
#There could also be configmaps in Base, which would make these overlays
configmaps:
- type: env
namePrefix: app-env
- name: app-env
env: configmap/app.env
- type: file
namePrefix: app-config
- name: app-config
files:
- configmap/app-init.ini
#There could be secrets in Base, if just using a fork/rebase workflow
secrets:
- type: tls
namePrefix: app-tls
tls:
certFile: secret/tls.cert
keyFile: secret/tls.key
tlsSecrets:
- name: app-tls
certFile: secret/tls.cert
keyFile: secret/tls.key
recursive: false
prune: true # Id make this the default

View File

@ -86,8 +86,8 @@ func populateConfigMapAndSecretMap(manifest *manifest.Manifest, m map[gvkn.Group
}
}
for _, secret := range manifest.Secrets {
unstructuredSecret, nameWithHash, err := cutil.MakeSecretAndGenerateName(secret)
for _, secret := range manifest.GenericSecrets {
unstructuredSecret, nameWithHash, err := cutil.MakeGenericSecretAndGenerateName(secret)
if err != nil {
return err
}
@ -96,6 +96,18 @@ func populateConfigMapAndSecretMap(manifest *manifest.Manifest, m map[gvkn.Group
return err
}
}
for _, secret := range manifest.TLSSecrets {
unstructuredSecret, nameWithHash, err := cutil.MakeTLSSecretAndGenerateName(secret)
if err != nil {
return err
}
err = populateMap(m, unstructuredSecret, nameWithHash)
if err != nil {
return err
}
}
return nil
}

View File

@ -64,6 +64,44 @@ func makeUnstructuredEnvSecret(name string) *unstructured.Unstructured {
}
}
func makeUnstructuredTLSSecret(name string) *unstructured.Unstructured {
return &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": name,
"creationTimestamp": nil,
},
"type": string(corev1.SecretTypeTLS),
"data": map[string]interface{}{
"tls.key": base64.StdEncoding.EncodeToString([]byte(`-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBANLJhPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wo
k/4xIA+ui35/MmNartNuC+BdZ1tMuVCPFZcCAwEAAQJAEJ2N+zsR0Xn8/Q6twa4G
6OB1M1WO+k+ztnX/1SvNeWu8D6GImtupLTYgjZcHufykj09jiHmjHx8u8ZZB/o1N
MQIhAPW+eyZo7ay3lMz1V01WVjNKK9QSn1MJlb06h/LuYv9FAiEA25WPedKgVyCW
SmUwbPw8fnTcpqDWE3yTO3vKcebqMSsCIBF3UmVue8YU3jybC3NxuXq3wNm34R8T
xVLHwDXh/6NJAiEAl2oHGGLz64BuAfjKrqwz7qMYr9HCLIe/YsoWq/olzScCIQDi
D2lWusoe2/nEqfDVVWGWlyJ7yOmqaVm/iNUN9B2N2g==
-----END RSA PRIVATE KEY-----
`)),
"tls.crt": base64.StdEncoding.EncodeToString([]byte(`-----BEGIN CERTIFICATE-----
MIIB0zCCAX2gAwIBAgIJAI/M7BYjwB+uMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTIwOTEyMjE1MjAyWhcNMTUwOTEyMjE1MjAyWjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANLJ
hPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wok/4xIA+ui35/MmNa
rtNuC+BdZ1tMuVCPFZcCAwEAAaNQME4wHQYDVR0OBBYEFJvKs8RfJaXTH08W+SGv
zQyKn0H8MB8GA1UdIwQYMBaAFJvKs8RfJaXTH08W+SGvzQyKn0H8MAwGA1UdEwQF
MAMBAf8wDQYJKoZIhvcNAQEFBQADQQBJlffJHybjDGxRMqaRmDhX0+6v02TUKZsW
r5QuVbpQhH6u+0UgcW0jp9QwpxoPTLTWGXEWBBBurxFwiCBhkQ+V
-----END CERTIFICATE-----
`)),
},
},
}
}
func TestPopulateMap(t *testing.T) {
expectedMap := map[gvkn.GroupVersionKindName]*unstructured.Unstructured{
{
@ -80,6 +118,13 @@ func TestPopulateMap(t *testing.T) {
},
Name: "envSecret",
}: makeUnstructuredEnvSecret("newNameSecret"),
{
GVK: schema.GroupVersionKind{
Version: "v1",
Kind: "Secret",
},
Name: "tlsSecret",
}: makeUnstructuredTLSSecret("newNameTLSSecret"),
}
m := map[gvkn.GroupVersionKindName]*unstructured.Unstructured{}
@ -91,6 +136,10 @@ func TestPopulateMap(t *testing.T) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = populateMap(m, makeUnstructuredTLSSecret("tlsSecret"), "newNameTLSSecret")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(m, expectedMap) {
t.Fatalf("%#v\ndoesn't match expected\n%#v\n", m, expectedMap)
@ -107,18 +156,16 @@ func TestPopulateMapOfConfigMapAndSecret(t *testing.T) {
manifest := &manifest.Manifest{
Configmaps: []manifest.ConfigMap{
{
Type: "env",
NamePrefix: "envConfigMap",
Generic: manifest.Generic{
Name: "envConfigMap",
DataSources: manifest.DataSources{
EnvSource: "examples/simple/instances/exampleinstance/configmap/app.env",
},
},
},
Secrets: []manifest.Secret{
GenericSecrets: []manifest.GenericSecret{
{
Type: "env",
NamePrefix: "envSecret",
Generic: manifest.Generic{
Name: "envSecret",
DataSources: manifest.DataSources{
EnvSource: "examples/simple/instances/exampleinstance/configmap/app.env",
},
},