Merge pull request #200 from mengqiy/configmap_secret
configmap and secret helper functions
This commit is contained in:
commit
7fdbf30305
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
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 kinflate
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
manifest "k8s.io/kubectl/pkg/apis/manifest/v1alpha1"
|
||||
"k8s.io/kubectl/pkg/kinflate/configmapandsecret"
|
||||
"k8s.io/kubectl/pkg/kinflate/hash"
|
||||
)
|
||||
|
||||
func populateMap(m map[groupVersionKindName]newNameObject, obj runtime.Object, newName string) error {
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldName := accessor.GetName()
|
||||
gvk := obj.GetObjectKind().GroupVersionKind()
|
||||
gvkn := groupVersionKindName{
|
||||
gvk: gvk,
|
||||
name: oldName,
|
||||
}
|
||||
if _, found := m[gvkn]; found {
|
||||
return fmt.Errorf("cannot use a duplicate name %q for %s", oldName, gvk)
|
||||
}
|
||||
accessor.SetName(newName)
|
||||
m[gvkn] = newNameObject{
|
||||
newName: newName,
|
||||
obj: obj,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func populateMapOfConfigMapAndSecret(r *resource, m map[groupVersionKindName]newNameObject) error {
|
||||
for _, cm := range r.configmaps {
|
||||
corev1cm, err := constructConfigMap(cm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h, err := hash.ConfigMapHash(corev1cm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nameWithHash := fmt.Sprintf("%s-%s", corev1cm.Name, h)
|
||||
err = populateMap(m, corev1cm, nameWithHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, secret := range r.secrets {
|
||||
corev1secret, err := constructSecret(secret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h, err := hash.SecretHash(corev1secret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nameWithHash := fmt.Sprintf("%s-%s", corev1secret.Name, h)
|
||||
err = populateMap(m, corev1secret, nameWithHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func constructConfigMap(cm manifest.ConfigMap) (*corev1.ConfigMap, error) {
|
||||
corev1cm := &corev1.ConfigMap{}
|
||||
corev1cm.APIVersion = "v1"
|
||||
corev1cm.Kind = "ConfigMap"
|
||||
corev1cm.Name = cm.NamePrefix
|
||||
corev1cm.Data = map[string]string{}
|
||||
var err error
|
||||
switch cm.Type {
|
||||
case "env":
|
||||
err = configmapandsecret.HandleConfigMapFromEnvFileSource(corev1cm, cm.EnvSource)
|
||||
case "file":
|
||||
err = configmapandsecret.HandleConfigMapFromFileSources(corev1cm, cm.FileSources)
|
||||
case "literal":
|
||||
err = configmapandsecret.HandleConfigMapFromLiteralSources(corev1cm, cm.LiteralSources)
|
||||
default:
|
||||
err = fmt.Errorf("unknown type of configmap: %v", cm.Type)
|
||||
}
|
||||
return corev1cm, err
|
||||
}
|
||||
|
||||
func constructSecret(secret manifest.Secret) (*corev1.Secret, error) {
|
||||
corev1secret := &corev1.Secret{}
|
||||
corev1secret.APIVersion = "v1"
|
||||
corev1secret.Kind = "Secret"
|
||||
corev1secret.Name = secret.NamePrefix
|
||||
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 {
|
||||
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 = configmapandsecret.HandleFromEnvFileSource(corev1secret, secret.EnvSource)
|
||||
case "file":
|
||||
err = configmapandsecret.HandleFromFileSources(corev1secret, secret.FileSources)
|
||||
case "literal":
|
||||
err = configmapandsecret.HandleFromLiteralSources(corev1secret, secret.LiteralSources)
|
||||
default:
|
||||
err = fmt.Errorf("unknown type of secret: %v", secret.Type)
|
||||
}
|
||||
return corev1secret, err
|
||||
}
|
||||
|
||||
func validateTls(cert, key string) error {
|
||||
if len(key) == 0 {
|
||||
return fmt.Errorf("key must be specified")
|
||||
}
|
||||
if len(cert) == 0 {
|
||||
return fmt.Errorf("certificate must be specified")
|
||||
}
|
||||
if _, err := tls.LoadX509KeyPair(cert, key); err != nil {
|
||||
return fmt.Errorf("failed to load key pair %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,379 @@
|
|||
/*
|
||||
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 kinflate
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
manifest "k8s.io/kubectl/pkg/apis/manifest/v1alpha1"
|
||||
)
|
||||
|
||||
var envConfigMap = &corev1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "envConfigMap",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"DB_USERNAME": "admin",
|
||||
"DB_PASSWORD": "somepw",
|
||||
},
|
||||
}
|
||||
|
||||
var fileConfigMap = &corev1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "fileConfigMap",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"app-init.ini": `FOO=bar
|
||||
BAR=baz
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
var literalConfigMap = &corev1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "literalConfigMap",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"a": "x",
|
||||
"b": "y",
|
||||
},
|
||||
}
|
||||
|
||||
var tlsSecret = &corev1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Secret",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "tlsSecret",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
corev1.TLSCertKey: []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-----
|
||||
`),
|
||||
corev1.TLSPrivateKeyKey: []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-----
|
||||
`),
|
||||
},
|
||||
Type: corev1.SecretTypeTLS,
|
||||
}
|
||||
|
||||
var envSecret = &corev1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Secret",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "envSecret",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"DB_USERNAME": []byte("admin"),
|
||||
"DB_PASSWORD": []byte("somepw"),
|
||||
},
|
||||
Type: corev1.SecretTypeOpaque,
|
||||
}
|
||||
|
||||
var fileSecret = &corev1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Secret",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "fileSecret",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"app-init.ini": []byte(`FOO=bar
|
||||
BAR=baz
|
||||
`),
|
||||
},
|
||||
Type: corev1.SecretTypeOpaque,
|
||||
}
|
||||
|
||||
var literalSecret = &corev1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Secret",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "literalSecret",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"a": []byte("x"),
|
||||
"b": []byte("y"),
|
||||
},
|
||||
Type: corev1.SecretTypeOpaque,
|
||||
}
|
||||
|
||||
func TestConstructConfigMap(t *testing.T) {
|
||||
type testCase struct {
|
||||
description string
|
||||
input manifest.ConfigMap
|
||||
expected *corev1.ConfigMap
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
description: "construct config map from env",
|
||||
input: manifest.ConfigMap{
|
||||
Type: "env",
|
||||
NamePrefix: "envConfigMap",
|
||||
Generic: manifest.Generic{
|
||||
EnvSource: "examples/simple/instances/exampleinstance/configmap/app.env",
|
||||
},
|
||||
},
|
||||
expected: envConfigMap,
|
||||
},
|
||||
{
|
||||
description: "construct config map from file",
|
||||
input: manifest.ConfigMap{
|
||||
Type: "file",
|
||||
NamePrefix: "fileConfigMap",
|
||||
Generic: manifest.Generic{
|
||||
FileSources: []string{"examples/simple/instances/exampleinstance/configmap/app-init.ini"},
|
||||
},
|
||||
},
|
||||
expected: fileConfigMap,
|
||||
},
|
||||
{
|
||||
description: "construct config map from literal",
|
||||
input: manifest.ConfigMap{
|
||||
Type: "literal",
|
||||
NamePrefix: "literalConfigMap",
|
||||
Generic: manifest.Generic{
|
||||
LiteralSources: []string{"a=x", "b=y"},
|
||||
},
|
||||
},
|
||||
expected: literalConfigMap,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
cm, err := constructConfigMap(tc.input)
|
||||
if err != nil {
|
||||
t.Fatalf("unepxected 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 TestConstructSecret(t *testing.T) {
|
||||
type testCase struct {
|
||||
description string
|
||||
input manifest.Secret
|
||||
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: tlsSecret,
|
||||
},
|
||||
{
|
||||
description: "construct secret from env",
|
||||
input: manifest.Secret{
|
||||
Type: "env",
|
||||
NamePrefix: "envSecret",
|
||||
Generic: manifest.Generic{
|
||||
EnvSource: "examples/simple/instances/exampleinstance/configmap/app.env",
|
||||
},
|
||||
},
|
||||
expected: envSecret,
|
||||
},
|
||||
{
|
||||
description: "construct secret from file",
|
||||
input: manifest.Secret{
|
||||
Type: "file",
|
||||
NamePrefix: "fileSecret",
|
||||
Generic: manifest.Generic{
|
||||
FileSources: []string{"examples/simple/instances/exampleinstance/configmap/app-init.ini"},
|
||||
},
|
||||
},
|
||||
expected: fileSecret,
|
||||
},
|
||||
{
|
||||
description: "construct secret from literal",
|
||||
input: manifest.Secret{
|
||||
Type: "literal",
|
||||
NamePrefix: "literalSecret",
|
||||
Generic: manifest.Generic{
|
||||
LiteralSources: []string{"a=x", "b=y"},
|
||||
},
|
||||
},
|
||||
expected: literalSecret,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
cm, err := constructSecret(tc.input)
|
||||
if err != nil {
|
||||
t.Fatalf("unepxected 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 TestPopulateMap(t *testing.T) {
|
||||
anotherCm := literalConfigMap.DeepCopy()
|
||||
expectedMap := map[groupVersionKindName]newNameObject{
|
||||
groupVersionKindName{
|
||||
gvk: schema.GroupVersionKind{
|
||||
Version: "v1",
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
name: "literalConfigMap",
|
||||
}: {
|
||||
newName: "newconfigmap",
|
||||
obj: literalConfigMap,
|
||||
},
|
||||
groupVersionKindName{
|
||||
gvk: schema.GroupVersionKind{
|
||||
Version: "v1",
|
||||
Kind: "Secret",
|
||||
},
|
||||
name: "tlsSecret",
|
||||
}: {
|
||||
newName: "newsecret",
|
||||
obj: tlsSecret,
|
||||
},
|
||||
}
|
||||
m := map[groupVersionKindName]newNameObject{}
|
||||
|
||||
err := populateMap(m, literalConfigMap, "newconfigmap")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
err = populateMap(m, tlsSecret, "newsecret")
|
||||
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)
|
||||
}
|
||||
|
||||
err = populateMap(m, anotherCm, "newconfigmap")
|
||||
if err == nil || !strings.Contains(err.Error(), "duplicate name") {
|
||||
t.Fatalf("expected error to contain %q, but got: %v", "duplicate name", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPopulateMapOfConfigMapAndSecret(t *testing.T) {
|
||||
m := map[groupVersionKindName]newNameObject{}
|
||||
r := &resource{
|
||||
configmaps: []manifest.ConfigMap{
|
||||
{
|
||||
Type: "literal",
|
||||
NamePrefix: "literalConfigMap",
|
||||
Generic: manifest.Generic{
|
||||
LiteralSources: []string{
|
||||
"a=x",
|
||||
"b=y",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
secrets: []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",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
literalConfigMapWithNewName := literalConfigMap.DeepCopy()
|
||||
literalConfigMapWithNewName.Name = "literalConfigMap-c8tc8tb6b7"
|
||||
tlsSecretWithNewName := tlsSecret.DeepCopy()
|
||||
tlsSecretWithNewName.Name = "tlsSecret-h4m4f95g75"
|
||||
expectedMap := map[groupVersionKindName]newNameObject{
|
||||
groupVersionKindName{
|
||||
gvk: schema.GroupVersionKind{
|
||||
Version: "v1",
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
name: "literalConfigMap",
|
||||
}: {
|
||||
newName: "literalConfigMap-c8tc8tb6b7",
|
||||
obj: literalConfigMapWithNewName,
|
||||
},
|
||||
groupVersionKindName{
|
||||
gvk: schema.GroupVersionKind{
|
||||
Version: "v1",
|
||||
Kind: "Secret",
|
||||
},
|
||||
name: "tlsSecret",
|
||||
}: {
|
||||
newName: "tlsSecret-h4m4f95g75",
|
||||
obj: tlsSecretWithNewName,
|
||||
},
|
||||
}
|
||||
err := populateMapOfConfigMapAndSecret(r, m)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected erorr: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(m, expectedMap) {
|
||||
t.Fatalf("%#v\ndoesn't match expected\n%#v\n", m, expectedMap)
|
||||
}
|
||||
}
|
|
@ -21,15 +21,27 @@ import (
|
|||
"io/ioutil"
|
||||
"path"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
manifest "k8s.io/kubectl/pkg/apis/manifest/v1alpha1"
|
||||
)
|
||||
|
||||
const kubeManifestFileName = "Kube-manifest.yaml"
|
||||
|
||||
type resource struct {
|
||||
resources []string
|
||||
configmaps []manifest.ConfigMap
|
||||
secrets []manifest.Secret
|
||||
}
|
||||
|
||||
type newNameObject struct {
|
||||
newName string
|
||||
obj runtime.Object
|
||||
}
|
||||
|
||||
// loadBaseAndOverlayPkg returns:
|
||||
// - List of FilenameOptions, each FilenameOptions contains all the files and whether recursive for each base defined in overlay kube-manifest.yaml.
|
||||
// - Fileoptions for overlay.
|
||||
|
|
Loading…
Reference in New Issue