Unverified Commit e85f5776 authored by bryanl's avatar bryanl
Browse files

Add apiVersion/kind to app.override.yaml



Fixes #513
Signed-off-by: default avatarbryanl <bryanliles@gmail.com>
parent d14e9326
......@@ -82,37 +82,12 @@ func (ba *baseApp) save() error {
return errors.Wrapf(err, "write %s", ba.configPath())
}
if err = cleanOverride(ba.fs, ba.root); err != nil {
if err = removeOverride(ba.fs, ba.root); err != nil {
return errors.Wrap(err, "clean overrides")
}
hasOverrides := false
if len(ba.overrides.Environments) > 0 || len(ba.overrides.Registries) > 0 {
hasOverrides = true
}
if hasOverrides {
overrideData, err := yaml.Marshal(ba.overrides)
if err != nil {
return errors.Wrap(err, "convert override configuration to YAML")
}
if err = afero.WriteFile(ba.fs, overridePath(ba.root), overrideData, DefaultFilePermissions); err != nil {
return errors.Wrapf(err, "write %s", overridePath(ba.root))
}
}
return nil
}
func (ba *baseApp) cleanOverride() error {
exists, err := afero.Exists(ba.fs, ba.overridePath())
if err != nil {
return err
}
if exists {
return ba.fs.Remove(ba.overridePath())
if ba.overrides.IsDefined() {
return SaveOverride(defaultYAMLEncoder, ba.fs, ba.root, ba.overrides)
}
return nil
......@@ -158,12 +133,8 @@ func (ba *baseApp) load() error {
return errors.Wrapf(err, "unmarshal override YAML config")
}
for k := range override.Registries {
override.Registries[k].isOverride = true
}
for k := range override.Environments {
override.Environments[k].isOverride = true
if err = override.Validate(); err != nil {
return errors.Wrap(err, "validating override")
}
if len(override.Environments) == 0 {
......@@ -174,6 +145,14 @@ func (ba *baseApp) load() error {
override.Registries = RegistryRefSpecs{}
}
for k := range override.Registries {
override.Registries[k].isOverride = true
}
for k := range override.Environments {
override.Environments[k].isOverride = true
}
}
ba.overrides = &override
......
......@@ -136,3 +136,15 @@ func Test_baseApp_load_override(t *testing.T) {
_, ok := ba.overrides.Registries["new"]
require.True(t, ok)
}
func Test_baseApp_load_override_invalid(t *testing.T) {
fs := afero.NewMemMapFs()
stageFile(t, fs, "app010_app.yaml", "/app.yaml")
stageFile(t, fs, "add-registry-override-invalid.yaml", "/app.override.yaml")
ba := newBaseApp(fs, "/")
err := ba.load()
require.Error(t, err)
}
package app
import (
"io"
"github.com/ghodss/yaml"
"github.com/pkg/errors"
)
// Encoder writes items to a serialized form.
type Encoder interface {
// Encode writes an item to a stream. Implementations may return errors
// if the data to be encoded is invalid.
Encode(i interface{}, w io.Writer) error
}
var (
defaultYAMLEncoder = &YAMLEncoder{}
)
// YAMLEncoder write items to a serialized form in YAML format.
type YAMLEncoder struct{}
// Encode encodes data in yaml format.
func (e *YAMLEncoder) Encode(i interface{}, w io.Writer) error {
b, err := yaml.Marshal(i)
if err != nil {
return errors.Wrap(err, "encoding data")
}
_, err = w.Write(b)
return err
}
......@@ -15,8 +15,63 @@
package app
import (
"os"
"github.com/pkg/errors"
"github.com/spf13/afero"
)
const (
// overrideKind is the override resource type.
overrideKind = "ksonnet.io/app-override"
// overrideVersion is the version of the override resource.
overrideVersion = "0.1.0"
)
// Override defines overrides to ksonnet project configurations.
type Override struct {
Kind string `json:"kind"`
APIVersion string `json:"apiVersion"`
Environments EnvironmentSpecs `json:"environments,omitempty"`
Registries RegistryRefSpecs `json:"registries,omitempty"`
}
// Validate validates an Override.
func (o *Override) Validate() error {
if o.Kind != overrideKind {
return errors.Errorf("app override has unexpected kind")
}
if o.APIVersion != overrideVersion {
return errors.Errorf("app override has unexpected apiVersion")
}
return nil
}
// IsDefined returns true if the override has environments or registries defined.
func (o *Override) IsDefined() bool {
return len(o.Environments) > 0 || len(o.Registries) > 0
}
// SaveOverride saves the override to the filesystem.
func SaveOverride(encoder Encoder, fs afero.Fs, root string, o *Override) error {
if o == nil {
return errors.New("override was nil")
}
o.APIVersion = overrideVersion
o.Kind = overrideKind
f, err := fs.OpenFile(overridePath(root), os.O_WRONLY|os.O_CREATE, DefaultFilePermissions)
if err != nil {
return err
}
if err := encoder.Encode(o, f); err != nil {
return errors.Wrap(err, "encoding override")
}
return nil
}
package app
import (
"io"
"testing"
"github.com/pkg/errors"
"github.com/spf13/afero"
"github.com/stretchr/testify/require"
)
func TestOverride_Validate(t *testing.T) {
cases := []struct {
name string
o Override
isErr bool
}{
{
name: "valid override",
o: Override{Kind: overrideKind, APIVersion: overrideVersion},
},
{
name: "missing kind",
o: Override{APIVersion: overrideVersion},
isErr: true,
},
{
name: "invalid kind",
o: Override{Kind: "invalid", APIVersion: overrideVersion},
isErr: true,
},
{
name: "missing version",
o: Override{Kind: overrideKind},
isErr: true,
},
{
name: "invalid version",
o: Override{APIVersion: "invalid", Kind: overrideKind},
isErr: true,
},
{
name: "missing kind and version",
o: Override{},
isErr: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
err := tc.o.Validate()
if tc.isErr {
require.Error(t, err)
return
}
require.NoError(t, err)
})
}
}
func TestSaveOverride(t *testing.T) {
cases := []struct {
name string
o *Override
encoder Encoder
isErr bool
}{
{
name: "save override",
o: &Override{},
encoder: defaultYAMLEncoder,
},
{
name: "encode error",
o: &Override{},
encoder: &failEncoder{},
isErr: true,
},
{
name: "override is nil",
isErr: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
fs := afero.NewMemMapFs()
err := SaveOverride(tc.encoder, fs, "/", tc.o)
if tc.isErr {
require.Error(t, err)
return
}
require.NoError(t, err)
})
}
}
type failEncoder struct{}
func (e *failEncoder) Encode(interface{}, io.Writer) error {
return errors.Errorf("fail")
}
......@@ -126,9 +126,9 @@ func read(fs afero.Fs, root string) (*Spec, error) {
// Write writes the provided spec to file system.
func write(fs afero.Fs, appRoot string, spec *Spec) error {
hasOverrides := false
o := Override{
Kind: overrideKind,
APIVersion: overrideVersion,
Environments: EnvironmentSpecs{},
Registries: RegistryRefSpecs{},
}
......@@ -140,7 +140,6 @@ func write(fs afero.Fs, appRoot string, spec *Spec) error {
for k, v := range spec.Environments {
if v.IsOverride() {
hasOverrides = true
o.Environments[k] = v
overrideKeys["environments"] = append(overrideKeys["environments"], k)
}
......@@ -148,7 +147,6 @@ func write(fs afero.Fs, appRoot string, spec *Spec) error {
for k, v := range spec.Registries {
if v.IsOverride() {
hasOverrides = true
o.Registries[k] = v
overrideKeys["registries"] = append(overrideKeys["registries"], k)
}
......@@ -171,25 +169,18 @@ func write(fs afero.Fs, appRoot string, spec *Spec) error {
return errors.Wrap(err, "write app.yaml")
}
if err = cleanOverride(fs, appRoot); err != nil {
if err = removeOverride(fs, appRoot); err != nil {
return errors.Wrap(err, "clean overrides")
}
if hasOverrides {
overrideConfig, err := yaml.Marshal(&o)
if err != nil {
return errors.Wrap(err, "convert app override configuration to YAML")
}
if err = afero.WriteFile(fs, overridePath(appRoot), overrideConfig, DefaultFilePermissions); err != nil {
return errors.Wrap(err, "write app.override.yaml")
}
if o.IsDefined() {
return SaveOverride(defaultYAMLEncoder, fs, appRoot, &o)
}
return nil
}
func cleanOverride(fs afero.Fs, appRoot string) error {
func removeOverride(fs afero.Fs, appRoot string) error {
exists, err := afero.Exists(fs, overridePath(appRoot))
if err != nil {
return err
......
registries:
new:
protocol: ""
uri: ""
apiVersion: 0.1.0
kind: ksonnet.io/app-override
registries:
new:
protocol: ""
......
apiVersion: 0.1.0
environments:
b:
destination: null
k8sVersion: ""
path: ""
kind: ksonnet.io/app-override
registries:
b:
protocol: ""
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment