Merge pull request #256 from droot/add-configmap

Add configmap
This commit is contained in:
k8s-ci-robot 2018-02-05 20:12:08 -08:00 committed by GitHub
commit 9b8afdfec9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 297 additions and 51 deletions

View File

@ -17,18 +17,15 @@ limitations under the License.
package commands package commands
import ( import (
"errors"
"fmt"
"io" "io"
"github.com/ghodss/yaml"
"errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
manifest "k8s.io/kubectl/pkg/apis/manifest/v1alpha1"
"k8s.io/kubectl/pkg/kinflate/constants"
"k8s.io/kubectl/pkg/kinflate/util/fs"
"fmt" "k8s.io/kubectl/pkg/kinflate/constants"
kutil "k8s.io/kubectl/pkg/kinflate/util"
"k8s.io/kubectl/pkg/kinflate/util/fs"
) )
type addResourceOptions struct { type addResourceOptions struct {
@ -90,15 +87,8 @@ func (o *addResourceOptions) RunAddResource(out, errOut io.Writer, fsys fs.FileS
return err return err
} }
content, err := fsys.ReadFile(constants.KubeManifestFileName) loader := kutil.ManifestLoader{FS: fsys}
if err != nil { m, err := loader.Read(constants.KubeManifestFileName)
return err
}
// TODO: Refactor to a common location you guys!
// See pkg/kinflate/util.go:loadManifestPkg
var m manifest.Manifest
err = yaml.Unmarshal(content, &m)
if err != nil { if err != nil {
return err return err
} }
@ -109,14 +99,5 @@ func (o *addResourceOptions) RunAddResource(out, errOut io.Writer, fsys fs.FileS
m.Resources = append(m.Resources, o.resourceFilePath) m.Resources = append(m.Resources, o.resourceFilePath)
bytes, err := yaml.Marshal(m) return loader.Write(constants.KubeManifestFileName, m)
if err != nil {
return err
}
err = fsys.WriteFile(constants.KubeManifestFileName, bytes)
if err != nil {
return err
}
return nil
} }

View File

@ -17,8 +17,13 @@ limitations under the License.
package commands package commands
import ( import (
"fmt"
"io" "io"
manifest "k8s.io/kubectl/pkg/apis/manifest/v1alpha1"
"k8s.io/kubectl/pkg/kinflate/configmapandsecret"
"k8s.io/kubectl/pkg/kinflate/constants"
kutil "k8s.io/kubectl/pkg/kinflate/util"
"k8s.io/kubectl/pkg/kinflate/util/fs" "k8s.io/kubectl/pkg/kinflate/util/fs"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -46,9 +51,21 @@ func NewCmdAddConfigMap(errOut io.Writer, fsys fs.FileSystem) *cobra.Command {
return err return err
} }
// TODO(apelisse,droot): Do something with that config. // Load in the manifest file.
loader := kutil.ManifestLoader{FS: fsys}
m, err := loader.Read(constants.KubeManifestFileName)
if err != nil {
return err
}
return nil // Add the config map to the manifest.
err = addConfigMap(m, config)
if err != nil {
return err
}
// Write out the manifest with added configmap.
return loader.Write(constants.KubeManifestFileName, m)
}, },
} }
@ -58,3 +75,46 @@ func NewCmdAddConfigMap(errOut io.Writer, fsys fs.FileSystem) *cobra.Command {
return cmd return cmd
} }
// addConfigMap updates a configmap within a manifest, using the data in config.
// Note: error may leave manifest in an undefined state. Suggest passing a copy
// of manifest.
func addConfigMap(m *manifest.Manifest, config dataConfig) error {
cm := getOrCreateConfigMap(m, config.Name)
err := mergeData(&cm.DataSources, config)
if err != nil {
return err
}
// Validate manifest's configmap by trying to create corev1.configmap.
_, _, err = configmapandsecret.MakeConfigmapAndGenerateName(*cm)
if err != nil {
return err
}
return nil
}
func getOrCreateConfigMap(m *manifest.Manifest, name string) *manifest.ConfigMap {
for i, v := range m.Configmaps {
if name == v.Name {
return &m.Configmaps[i]
}
}
// config map not found, create new one and add it to the manifest.
cm := &manifest.ConfigMap{Name: name}
m.Configmaps = append(m.Configmaps, *cm)
return &m.Configmaps[len(m.Configmaps)-1]
}
func mergeData(src *manifest.DataSources, config dataConfig) error {
src.LiteralSources = append(src.LiteralSources, config.LiteralSources...)
src.FileSources = append(src.FileSources, config.FileSources...)
if src.EnvSource != "" && src.EnvSource != config.EnvFileSource {
return fmt.Errorf("updating existing env source '%s' not allowed.", src.EnvSource)
}
src.EnvSource = config.EnvFileSource
return nil
}

View File

@ -19,6 +19,7 @@ package commands
import ( import (
"testing" "testing"
manifest "k8s.io/kubectl/pkg/apis/manifest/v1alpha1"
"k8s.io/kubectl/pkg/kinflate/util/fs" "k8s.io/kubectl/pkg/kinflate/util/fs"
) )
@ -27,3 +28,102 @@ func TestNewAddConfigMapIsNotNil(t *testing.T) {
t.Fatal("NewCmdAddConfigMap shouldn't be nil") t.Fatal("NewCmdAddConfigMap shouldn't be nil")
} }
} }
func TestGetOrCreateConfigMap(t *testing.T) {
cmName := "test-config-name"
manifest := &manifest.Manifest{
NamePrefix: "test-name-prefix",
}
if len(manifest.Configmaps) != 0 {
t.Fatal("Initial manifest should not have any configmaps")
}
cm := getOrCreateConfigMap(manifest, cmName)
if cm == nil {
t.Fatalf("ConfigMap should always be non-nil")
}
if len(manifest.Configmaps) != 1 {
t.Fatalf("Manifest should have newly created configmap")
}
if &manifest.Configmaps[len(manifest.Configmaps)-1] != cm {
t.Fatalf("Pointer address for newly inserted configmap should be same")
}
existingCM := getOrCreateConfigMap(manifest, cmName)
if existingCM != cm {
t.Fatalf("should have returned an existing cm with name: %v", cmName)
}
if len(manifest.Configmaps) != 1 {
t.Fatalf("Should not insert configmap for an existing name: %v", cmName)
}
}
func TestMergeData_LiteralSources(t *testing.T) {
ds := &manifest.DataSources{}
err := mergeData(ds, dataConfig{LiteralSources: []string{"k1=v1"}})
if err != nil {
t.Fatalf("Merge initial literal source should not return error")
}
if len(ds.LiteralSources) != 1 {
t.Fatalf("Initial literal source should have been added")
}
err = mergeData(ds, dataConfig{LiteralSources: []string{"k2=v2"}})
if err != nil {
t.Fatalf("Merge second literal source should not return error")
}
if len(ds.LiteralSources) != 2 {
t.Fatalf("Second literal source should have been added")
}
}
func TestMergeData_FileSources(t *testing.T) {
ds := &manifest.DataSources{}
err := mergeData(ds, dataConfig{FileSources: []string{"file1"}})
if err != nil {
t.Fatalf("Merge initial file source should not return error")
}
if len(ds.FileSources) != 1 {
t.Fatalf("Initial file source should have been added")
}
err = mergeData(ds, dataConfig{FileSources: []string{"file2"}})
if err != nil {
t.Fatalf("Merge second file source should not return error")
}
if len(ds.FileSources) != 2 {
t.Fatalf("Second file source should have been added")
}
}
func TestMergeData_EnvSource(t *testing.T) {
envFileName := "env1"
envFileName2 := "env2"
ds := &manifest.DataSources{}
err := mergeData(ds, dataConfig{EnvFileSource: envFileName})
if err != nil {
t.Fatalf("Merge initial env source should not return error")
}
if ds.EnvSource != envFileName {
t.Fatalf("Initial env source filename should have been added")
}
err = mergeData(ds, dataConfig{EnvFileSource: envFileName2})
if err == nil {
t.Fatalf("Updating env source should return an error")
}
}

View File

@ -29,6 +29,7 @@ type dataConfig struct {
// LiteralSources to derive the configMap/Secret from (optional) // LiteralSources to derive the configMap/Secret from (optional)
LiteralSources []string LiteralSources []string
// EnvFileSource to derive the configMap/Secret from (optional) // EnvFileSource to derive the configMap/Secret from (optional)
// TODO: Rationalize this name with Generic.EnvSource
EnvFileSource string EnvFileSource string
} }

View File

@ -23,8 +23,6 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"github.com/ghodss/yaml"
"strings" "strings"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
@ -40,23 +38,6 @@ import (
"k8s.io/kubectl/pkg/scheme" "k8s.io/kubectl/pkg/scheme"
) )
// loadManifestPkg loads a manifest file and parse it in to the Manifest object.
func loadManifestPkg(filename string) (*manifest.Manifest, error) {
bytes, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
var m manifest.Manifest
// TODO: support json
err = yaml.Unmarshal(bytes, &m)
if err != nil {
return nil, err
}
dir, _ := path.Split(filename)
adjustPathsForManifest(&m, []string{dir})
return &m, err
}
func populateMap(m map[gvkn.GroupVersionKindName]*unstructured.Unstructured, obj *unstructured.Unstructured, newName string) error { func populateMap(m map[gvkn.GroupVersionKindName]*unstructured.Unstructured, obj *unstructured.Unstructured, newName string) error {
accessor, err := meta.Accessor(obj) accessor, err := meta.Accessor(obj)
if err != nil { if err != nil {
@ -137,7 +118,7 @@ func LoadFromManifestPath(mPath string,
return nil, fmt.Errorf("expecting file: %q, but got: %q", constants.KubeManifestFileName, mPath) return nil, fmt.Errorf("expecting file: %q, but got: %q", constants.KubeManifestFileName, mPath)
} }
} }
manifest, err := loadManifestPkg(mPath) manifest, err := (&kutil.ManifestLoader{}).Read(mPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -195,7 +176,7 @@ func dirToMap(dirname string, into map[gvkn.GroupVersionKindName]*unstructured.U
case err != nil && !os.IsNotExist(err): case err != nil && !os.IsNotExist(err):
return err return err
case err == nil: case err == nil:
manifest, err := loadManifestPkg(kubeManifestFileAbsName) manifest, err := (&kutil.ManifestLoader{}).Read(kubeManifestFileAbsName)
if err != nil { if err != nil {
return err return err
} }

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package kinflate package util
import ( import (
"path" "path"

View File

@ -0,0 +1,68 @@
/*
Copyright 2017 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"
"path"
"github.com/ghodss/yaml"
manifest "k8s.io/kubectl/pkg/apis/manifest/v1alpha1"
"k8s.io/kubectl/pkg/kinflate/util/fs"
)
type ManifestLoader struct {
FS fs.FileSystem
}
func (m *ManifestLoader) fs() fs.FileSystem {
if m.FS == nil {
m.FS = fs.MakeRealFS()
}
return m.FS
}
// Read loads a manifest file and parse it in to the Manifest object.
func (m *ManifestLoader) Read(filename string) (*manifest.Manifest, error) {
bytes, err := m.fs().ReadFile(filename)
if err != nil {
return nil, err
}
var manifest manifest.Manifest
err = yaml.Unmarshal(bytes, &manifest)
if err != nil {
return nil, err
}
dir, _ := path.Split(filename)
adjustPathsForManifest(&manifest, []string{dir})
return &manifest, err
}
// Write dumps the Manifest object into a file. If manifest is nil, an
// error is returned.
func (m *ManifestLoader) Write(filename string, manifest *manifest.Manifest) error {
if manifest == nil {
return errors.New("util: failed to write passed-in nil manifest")
}
bytes, err := yaml.Marshal(manifest)
if err != nil {
return err
}
return m.fs().WriteFile(filename, bytes)
}

View File

@ -0,0 +1,55 @@
/*
Copyright 2017 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_test
import (
"reflect"
"testing"
manifest "k8s.io/kubectl/pkg/apis/manifest/v1alpha1"
kutil "k8s.io/kubectl/pkg/kinflate/util"
"k8s.io/kubectl/pkg/kinflate/util/fs"
)
func TestManifestLoader(t *testing.T) {
manifest := &manifest.Manifest{
NamePrefix: "prefix",
}
loader := kutil.ManifestLoader{FS: fs.MakeFakeFS()}
if err := loader.Write("my-manifest.yaml", manifest); err != nil {
t.Fatalf("Couldn't write manifest file: %v\n", err)
}
readManifest, err := loader.Read("my-manifest.yaml")
if err != nil {
t.Fatalf("Couldn't read manifest file: %v\n", err)
}
if !reflect.DeepEqual(manifest, readManifest) {
t.Fatal("Read manifest is different from written manifest")
}
}
func TestManifestLoaderEmptyFile(t *testing.T) {
manifest := &manifest.Manifest{
NamePrefix: "prefix",
}
loader := kutil.ManifestLoader{}
if loader.Write("", manifest) == nil {
t.Fatalf("Write to empty filename should fail")
}
}