Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pkg/model/config: only project versions >= v3 can use plugins config options #1628

Merged
merged 1 commit into from
Aug 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
246 changes: 145 additions & 101 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,67 +27,69 @@ import (
)

var _ = Describe("Config", func() {
It("should save correctly", func() {
var (
cfg Config
expectedConfigStr string
)

By("saving empty config")
Expect(cfg.Save()).To(HaveOccurred())

By("saving empty config with path")
cfg = Config{
fs: afero.NewMemMapFs(),
path: DefaultPath,
}
Expect(cfg.Save()).ToNot(HaveOccurred())
cfgBytes, err := afero.ReadFile(cfg.fs, DefaultPath)
Expect(err).NotTo(HaveOccurred())
Expect(string(cfgBytes)).To(Equal(expectedConfigStr))

By("saving config version 2")
cfg = Config{
Config: config.Config{
Version: config.Version2,
Repo: "github.com/example/project",
Domain: "example.com",
},
fs: afero.NewMemMapFs(),
path: DefaultPath,
}
expectedConfigStr = `domain: example.com
var (
cfg Config
expectedConfigStr string
)

Context("with valid keys", func() {
It("should save correctly", func() {

By("saving empty config")
Expect(cfg.Save()).To(HaveOccurred())

By("saving empty config with path")
cfg = Config{
fs: afero.NewMemMapFs(),
path: DefaultPath,
}
Expect(cfg.Save()).To(Succeed())
cfgBytes, err := afero.ReadFile(cfg.fs, DefaultPath)
Expect(err).NotTo(HaveOccurred())
Expect(string(cfgBytes)).To(Equal(expectedConfigStr))

By("saving config version 2")
cfg = Config{
Config: config.Config{
Version: config.Version2,
Repo: "github.com/example/project",
Domain: "example.com",
},
fs: afero.NewMemMapFs(),
path: DefaultPath,
}
expectedConfigStr = `domain: example.com
repo: github.com/example/project
version: "2"
`
Expect(cfg.Save()).ToNot(HaveOccurred())
cfgBytes, err = afero.ReadFile(cfg.fs, DefaultPath)
Expect(err).NotTo(HaveOccurred())
Expect(string(cfgBytes)).To(Equal(expectedConfigStr))

By("saving config version 2 with plugin config")
cfg = Config{
Config: config.Config{
Version: config.Version2,
Repo: "github.com/example/project",
Domain: "example.com",
Plugins: config.PluginConfigs{
"plugin-x": map[string]interface{}{
"data-1": "single plugin datum",
},
"plugin-y/v1": map[string]interface{}{
"data-1": "plugin value 1",
"data-2": "plugin value 2",
"data-3": []string{"plugin value 3", "plugin value 4"},
Expect(cfg.Save()).To(Succeed())
cfgBytes, err = afero.ReadFile(cfg.fs, DefaultPath)
Expect(err).NotTo(HaveOccurred())
Expect(string(cfgBytes)).To(Equal(expectedConfigStr))

By("saving config version 3-alpha with plugin config")
cfg = Config{
Config: config.Config{
Version: config.Version3Alpha,
Repo: "github.com/example/project",
Domain: "example.com",
Plugins: config.PluginConfigs{
"plugin-x": map[string]interface{}{
"data-1": "single plugin datum",
},
"plugin-y/v1": map[string]interface{}{
"data-1": "plugin value 1",
"data-2": "plugin value 2",
"data-3": []string{"plugin value 3", "plugin value 4"},
},
},
},
},
fs: afero.NewMemMapFs(),
path: DefaultPath,
}
expectedConfigStr = `domain: example.com
fs: afero.NewMemMapFs(),
path: DefaultPath,
}
expectedConfigStr = `domain: example.com
repo: github.com/example/project
version: "2"
version: "3-alpha"
plugins:
plugin-x:
data-1: single plugin datum
Expand All @@ -98,39 +100,39 @@ plugins:
- plugin value 3
- plugin value 4
`
Expect(cfg.Save()).ToNot(HaveOccurred())
cfgBytes, err = afero.ReadFile(cfg.fs, DefaultPath)
Expect(err).NotTo(HaveOccurred())
Expect(string(cfgBytes)).To(Equal(expectedConfigStr))
})
Expect(cfg.Save()).To(Succeed())
cfgBytes, err = afero.ReadFile(cfg.fs, DefaultPath)
Expect(err).NotTo(HaveOccurred())
Expect(string(cfgBytes)).To(Equal(expectedConfigStr))
})

It("should load correctly", func() {
var (
fs = afero.NewMemMapFs()
configStr string
expectedConfig config.Config
)
It("should load correctly", func() {
var (
fs = afero.NewMemMapFs()
configStr string
expectedConfig config.Config
)

By("loading config version 2")
configStr = `domain: example.com
By("loading config version 2")
configStr = `domain: example.com
repo: github.com/example/project
version: "2"`
expectedConfig = config.Config{
Version: config.Version2,
Repo: "github.com/example/project",
Domain: "example.com",
}
err := afero.WriteFile(fs, DefaultPath, []byte(configStr), os.ModePerm)
Expect(err).ToNot(HaveOccurred())
cfg, err := readFrom(fs, DefaultPath)
Expect(err).NotTo(HaveOccurred())
Expect(cfg).To(BeEquivalentTo(expectedConfig))

By("loading config version 2 with plugin config")
fs = afero.NewMemMapFs()
configStr = `domain: example.com
expectedConfig = config.Config{
Version: config.Version2,
Repo: "github.com/example/project",
Domain: "example.com",
}
err := afero.WriteFile(fs, DefaultPath, []byte(configStr), os.ModePerm)
Expect(err).ToNot(HaveOccurred())
cfg, err := readFrom(fs, DefaultPath)
Expect(err).NotTo(HaveOccurred())
Expect(cfg).To(BeEquivalentTo(expectedConfig))

By("loading config version 3-alpha with plugin config")
fs = afero.NewMemMapFs()
configStr = `domain: example.com
repo: github.com/example/project
version: "2"
version: "3-alpha"
plugins:
plugin-x:
data-1: single plugin datum
Expand All @@ -140,25 +142,67 @@ plugins:
data-3:
- "plugin value 3"
- "plugin value 4"`
expectedConfig = config.Config{
Version: config.Version2,
Repo: "github.com/example/project",
Domain: "example.com",
Plugins: config.PluginConfigs{
"plugin-x": map[string]interface{}{
"data-1": "single plugin datum",
expectedConfig = config.Config{
Version: config.Version2,
Repo: "github.com/example/project",
Domain: "example.com",
Plugins: config.PluginConfigs{
"plugin-x": map[string]interface{}{
"data-1": "single plugin datum",
},
"plugin-y/v1": map[string]interface{}{
"data-1": "plugin value 1",
"data-2": "plugin value 2",
"data-3": []interface{}{"plugin value 3", "plugin value 4"},
},
},
"plugin-y/v1": map[string]interface{}{
"data-1": "plugin value 1",
"data-2": "plugin value 2",
"data-3": []interface{}{"plugin value 3", "plugin value 4"},
}
err = afero.WriteFile(fs, DefaultPath, []byte(configStr), os.ModePerm)
Expect(err).ToNot(HaveOccurred())
cfg, err = readFrom(fs, DefaultPath)
Expect(err).NotTo(HaveOccurred())
Expect(cfg).To(Equal(expectedConfig))
})
})

Context("with invalid keys", func() {
It("should return a save error", func() {
By("saving config version 2 with plugin config")
cfg = Config{
Config: config.Config{
Version: config.Version2,
Repo: "github.com/example/project",
Domain: "example.com",
Plugins: config.PluginConfigs{
"plugin-x": map[string]interface{}{
"data-1": "single plugin datum",
},
},
},
},
}
err = afero.WriteFile(fs, DefaultPath, []byte(configStr), os.ModePerm)
Expect(err).ToNot(HaveOccurred())
cfg, err = readFrom(fs, DefaultPath)
Expect(err).NotTo(HaveOccurred())
Expect(cfg).To(Equal(expectedConfig))
fs: afero.NewMemMapFs(),
path: DefaultPath,
}
Expect(cfg.Save()).NotTo(Succeed())
})

It("should return a load error", func() {
var (
fs afero.Fs
configStr string
err error
)
By("loading config version 2 with plugin config")
fs = afero.NewMemMapFs()
configStr = `domain: example.com
repo: github.com/example/project
version: "3-alpha"
plugins:
plugin-x:
data-1: single plugin datum`
err = afero.WriteFile(fs, DefaultPath, []byte(configStr), os.ModePerm)
Expect(err).ToNot(HaveOccurred())
_, err = readFrom(fs, DefaultPath)
Expect(err).To(HaveOccurred())
})
})
})
29 changes: 15 additions & 14 deletions pkg/model/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@ func (c Config) Marshal() ([]byte, error) {
content = []byte{}
}
// Append extra fields to put them at the config's bottom, unless the
// project is v1 which does not support extra fields.
if !cfg.IsV1() && len(c.Plugins) != 0 {
// project version is < v3 which do not support a plugins field.
if cfg.IsV3() && len(c.Plugins) != 0 {
pluginConfigBytes, err := yaml.Marshal(Config{Plugins: c.Plugins})
if err != nil {
return nil, fmt.Errorf("error marshalling project configuration extra fields: %v", err)
Expand All @@ -169,20 +169,21 @@ func (c *Config) Unmarshal(b []byte) error {
if err := yaml.UnmarshalStrict(b, c); err != nil {
return fmt.Errorf("error unmarshalling project configuration: %v", err)
}
// v1 projects do not support extra fields.
if c.IsV1() {
// Project versions < v3 do not support a plugins field.
if !c.IsV3() {
c.Plugins = nil
}
return nil
}

// EncodePluginConfig encodes a config object into c by overwriting the existing
// object stored under key. This method is intended to be used for custom
// configuration objects.
// configuration objects, which were introduced in project version 3-alpha.
// EncodePluginConfig will return an error if used on any project version < v3.
func (c *Config) EncodePluginConfig(key string, configObj interface{}) error {
// Short-circuit v1
if c.IsV1() {
return fmt.Errorf("v1 project configs do not have extra fields")
// Short-circuit project versions < v3.
if !c.IsV3() {
return fmt.Errorf("project versions < v3 do not support extra fields")
}

// Get object's bytes and set them under key in extra fields.
Expand All @@ -201,13 +202,13 @@ func (c *Config) EncodePluginConfig(key string, configObj interface{}) error {
return nil
}

// DecodePluginConfig decodes a plugin config stored in c into configObj. This
// method is intended to be used for custom configuration objects.
// configObj must be a pointer.
// DecodePluginConfig decodes a plugin config stored in c into configObj, which must be a pointer
// This method is intended to be used for custom configuration objects, which were introduced
// in project version 3-alpha. EncodePluginConfig will return an error if used on any project version < v3.
func (c Config) DecodePluginConfig(key string, configObj interface{}) error {
// Short-circuit v1
if c.IsV1() {
return fmt.Errorf("v1 project configs do not have extra fields")
// Short-circuit project versions < v3.
if !c.IsV3() {
return fmt.Errorf("project versions < v3 do not support extra fields")
}
if len(c.Plugins) == 0 {
return nil
Expand Down
Loading