base_app.go 5.06 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// Copyright 2018 The ksonnet 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 app

import (
	"path/filepath"
bryanl's avatar
bryanl committed
20
	"sync"
21

bryanl's avatar
bryanl committed
22 23
	"github.com/ghodss/yaml"
	"github.com/pkg/errors"
24 25 26 27 28 29
	"github.com/spf13/afero"
)

type baseApp struct {
	root string
	fs   afero.Fs
bryanl's avatar
bryanl committed
30 31 32 33 34

	config    *Spec
	overrides *Override

	mu sync.Mutex
35 36 37 38
}

func newBaseApp(fs afero.Fs, root string) *baseApp {
	return &baseApp{
bryanl's avatar
bryanl committed
39 40 41 42 43 44 45
		fs:     fs,
		root:   root,
		config: &Spec{},
		overrides: &Override{
			Environments: EnvironmentSpecs{},
			Registries:   RegistryRefSpecs{},
		},
46 47 48
	}
}

bryanl's avatar
bryanl committed
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
func (ba *baseApp) CurrentEnvironment() string {
	currentPath := filepath.Join(ba.root, currentEnvName)
	data, err := afero.ReadFile(ba.fs, currentPath)
	if err != nil {
		return ""
	}

	return string(data)
}

func (ba *baseApp) SetCurrentEnvironment(name string) error {
	currentPath := filepath.Join(ba.root, currentEnvName)
	return afero.WriteFile(ba.fs, currentPath, []byte(name), DefaultFilePermissions)
}

bryanl's avatar
bryanl committed
64 65 66 67 68 69
func (ba *baseApp) configPath() string {
	return filepath.Join(ba.root, "app.yaml")
}

func (ba *baseApp) overridePath() string {
	return filepath.Join(ba.root, "app.override.yaml")
70 71
}

bryanl's avatar
bryanl committed
72 73 74 75 76
func (ba *baseApp) save() error {
	ba.mu.Lock()
	defer ba.mu.Unlock()

	configData, err := yaml.Marshal(ba.config)
77
	if err != nil {
bryanl's avatar
bryanl committed
78 79 80 81 82
		return errors.Wrap(err, "convert application configuration to YAML")
	}

	if err = afero.WriteFile(ba.fs, ba.configPath(), configData, DefaultFilePermissions); err != nil {
		return errors.Wrapf(err, "write %s", ba.configPath())
83 84
	}

85
	if err = removeOverride(ba.fs, ba.root); err != nil {
bryanl's avatar
bryanl committed
86 87 88
		return errors.Wrap(err, "clean overrides")
	}

89 90
	if ba.overrides.IsDefined() {
		return SaveOverride(defaultYAMLEncoder, ba.fs, ba.root, ba.overrides)
bryanl's avatar
bryanl committed
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
	}

	return nil
}

func (ba *baseApp) load() error {
	ba.mu.Lock()
	defer ba.mu.Unlock()

	configData, err := afero.ReadFile(ba.fs, ba.configPath())
	if err != nil {
		return errors.Wrapf(err, "read %s", ba.configPath())
	}

	var config Spec
	if err = yaml.Unmarshal(configData, &config); err != nil {
		return errors.Wrapf(err, "unmarshal application YAML config")
	}

	exists, err := afero.Exists(ba.fs, ba.overridePath())
111 112 113 114
	if err != nil {
		return err
	}

bryanl's avatar
bryanl committed
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
	if len(config.Environments) == 0 {
		config.Environments = EnvironmentSpecs{}
	}

	if len(config.Registries) == 0 {
		config.Registries = RegistryRefSpecs{}
	}

	override := Override{
		Environments: EnvironmentSpecs{},
		Registries:   RegistryRefSpecs{},
	}
	if exists {
		overrideData, err := afero.ReadFile(ba.fs, ba.overridePath())
		if err != nil {
			return errors.Wrapf(err, "read %s", ba.overridePath())
		}
		if err = yaml.Unmarshal(overrideData, &override); err != nil {
			return errors.Wrapf(err, "unmarshal override YAML config")
		}

136 137
		if err = override.Validate(); err != nil {
			return errors.Wrap(err, "validating override")
bryanl's avatar
bryanl committed
138 139 140 141 142 143 144 145 146 147
		}

		if len(override.Environments) == 0 {
			override.Environments = EnvironmentSpecs{}
		}

		if len(override.Registries) == 0 {
			override.Registries = RegistryRefSpecs{}
		}

148 149 150 151 152 153 154 155
		for k := range override.Registries {
			override.Registries[k].isOverride = true
		}

		for k := range override.Environments {
			override.Environments[k].isOverride = true
		}

bryanl's avatar
bryanl committed
156 157 158 159 160 161 162 163 164 165 166 167 168
	}

	ba.overrides = &override
	ba.config = &config

	return ba.config.validate()
}

func (ba *baseApp) AddRegistry(newReg *RegistryRefSpec, isOverride bool) error {
	if err := ba.load(); err != nil {
		return errors.Wrap(err, "load configuration")
	}

169 170 171 172
	if newReg.Name == "" {
		return ErrRegistryNameInvalid
	}

bryanl's avatar
bryanl committed
173 174 175 176 177 178 179 180 181 182 183 184 185 186
	if isOverride {
		_, exists := ba.overrides.Registries[newReg.Name]
		if exists {
			return ErrRegistryExists
		}

		newReg.isOverride = true

		ba.overrides.Registries[newReg.Name] = newReg
		return ba.save()
	}

	_, exists := ba.config.Registries[newReg.Name]
	if exists {
187 188 189
		return ErrRegistryExists
	}

bryanl's avatar
bryanl committed
190 191
	newReg.isOverride = false
	ba.config.Registries[newReg.Name] = newReg
192

bryanl's avatar
bryanl committed
193
	return ba.save()
194 195 196
}

func (ba *baseApp) UpdateLib(name string, libSpec *LibraryRefSpec) error {
bryanl's avatar
bryanl committed
197 198
	if err := ba.load(); err != nil {
		return errors.Wrap(err, "load configuration")
199 200
	}

bryanl's avatar
bryanl committed
201 202
	ba.config.Libraries[name] = libSpec
	return ba.save()
203 204 205 206 207 208 209 210 211 212 213
}

func (ba *baseApp) Fs() afero.Fs {
	return ba.fs
}

func (ba *baseApp) Root() string {
	return ba.root
}

func (ba *baseApp) EnvironmentParams(envName string) (string, error) {
bryanl's avatar
bryanl committed
214 215 216
	if envName == "" {
		return "", errors.New("environment name is blank")
	}
217 218 219
	envParamsPath := filepath.Join(ba.Root(), EnvironmentDirName, envName, "params.libsonnet")
	b, err := afero.ReadFile(ba.Fs(), envParamsPath)
	if err != nil {
bryanl's avatar
bryanl committed
220
		return "", errors.Wrapf(err, "unable to read params for environment %s", envName)
221 222 223 224
	}

	return string(b), nil
}