Skip to content

Commit

Permalink
feat: enable per-provider model-defaults and bootstrap-constraints
Browse files Browse the repository at this point in the history
  • Loading branch information
jnsgruk committed Nov 1, 2024
1 parent d5fb987 commit 864293b
Show file tree
Hide file tree
Showing 11 changed files with 195 additions and 66 deletions.
32 changes: 29 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,10 @@ working directory.
juju:
# (Optional): Channel from which to install Juju.
channel: <channel>
# (Optional): A map of model-defaults to set when bootstrapping Juju controllers.
# (Optional): A map of model-defaults to set when bootstrapping *all* Juju controllers.
model-defaults:
<model-default>: <value>
# (Optional): A map of bootstrap-constraints to set when bootstrapping Juju controllers.
# (Optional): A map of bootstrap-constraints to set when bootstrapping *all* Juju controllers.
bootstrap-constraints:
<bootstrap-constraint>: <value>

Expand All @@ -150,6 +150,12 @@ providers:
enable: true | false
# (Optional) Whether or not to bootstrap a controller onto MicroK8s.
bootstrap: true | false
# (Optional): A map of model-defaults to set when bootstrapping the Juju controller.
model-defaults:
<model-default>: <value>
# (Optional): A map of bootstrap-constraints to set when bootstrapping the Juju controller.
bootstrap-constraints:
<bootstrap-constraint>: <value>
# (Optional): Channel from which to install MicroK8s.
channel: <channel>
# (Optional): MicroK8s addons to enable.
Expand All @@ -164,6 +170,12 @@ providers:
bootstrap: true | false
# (Optional): Channel from which to install K8s.
channel: <channel>
# (Optional): A map of model-defaults to set when bootstrapping the Juju controller.
model-defaults:
<model-default>: <value>
# (Optional): A map of bootstrap-constraints to set when bootstrapping the Juju controller.
bootstrap-constraints:
<bootstrap-constraint>: <value>
# (Optional): K8s features to configure.
features:
<feature>:
Expand All @@ -177,6 +189,12 @@ providers:
bootstrap: true | false
# (Optional): Channel from which to install LXD.
channel: <channel>
# (Optional): A map of model-defaults to set when bootstrapping the Juju controller.
model-defaults:
<model-default>: <value>
# (Optional): A map of bootstrap-constraints to set when bootstrapping the Juju controller.
bootstrap-constraints:
<bootstrap-constraint>: <value>

# (Optional) Google provider configuration.
google:
Expand All @@ -187,6 +205,12 @@ providers:
# (Optional): File containing credentials for Google cloud.
# See below note on the credentials file format.
credentials-file: <path>
# (Optional): A map of model-defaults to set when bootstrapping the Juju controller.
model-defaults:
<model-default>: <value>
# (Optional): A map of bootstrap-constraints to set when bootstrapping the Juju controller.
bootstrap-constraints:
<bootstrap-constraint>: <value>

# (Optional) Additional host configuration.
host:
Expand Down Expand Up @@ -262,7 +286,7 @@ juju:
test-mode: "true"
automatically-retry-hooks: "false"
bootstrap-constraints:
root-size: 2G
arch: amd64
providers:
microk8s:
Expand All @@ -284,6 +308,8 @@ providers:
load-balancer:
l2-mode: true
cidrs: 10.64.140.43/32
bootstrap-constraints:
root-disk: 2G
lxd:
enable: true
Expand Down
36 changes: 22 additions & 14 deletions internal/config/config_format.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,32 +32,40 @@ type providerConfig struct {

// lxdConfig represents how LXD should be configured on the host.
type lxdConfig struct {
Enable bool `mapstructure:"enable"`
Bootstrap bool `mapstructure:"bootstrap"`
Channel string `mapstructure:"channel"`
Enable bool `mapstructure:"enable"`
Bootstrap bool `mapstructure:"bootstrap"`
Channel string `mapstructure:"channel"`
ModelDefaults map[string]string `mapstructure:"model-defaults"`
BootstrapConstraints map[string]string `mapstructure:"bootstrap-constraints"`
}

// googleConfig represents how Juju should be configured for Google Cloud use.
type googleConfig struct {
Enable bool `mapstructure:"enable"`
Bootstrap bool `mapstructure:"bootstrap"`
CredentialsFile string `mapstructure:"credentials-file"`
Enable bool `mapstructure:"enable"`
Bootstrap bool `mapstructure:"bootstrap"`
CredentialsFile string `mapstructure:"credentials-file"`
ModelDefaults map[string]string `mapstructure:"model-defaults"`
BootstrapConstraints map[string]string `mapstructure:"bootstrap-constraints"`
}

// microk8sConfig represents how MicroK8s should be configured on the host.
type microk8sConfig struct {
Enable bool `mapstructure:"enable"`
Bootstrap bool `mapstructure:"bootstrap"`
Channel string `mapstructure:"channel"`
Addons []string `mapstructure:"addons"`
Enable bool `mapstructure:"enable"`
Bootstrap bool `mapstructure:"bootstrap"`
Channel string `mapstructure:"channel"`
Addons []string `mapstructure:"addons"`
ModelDefaults map[string]string `mapstructure:"model-defaults"`
BootstrapConstraints map[string]string `mapstructure:"bootstrap-constraints"`
}

// k8sConfig represents how MicroK8s should be configured on the host.
type k8sConfig struct {
Enable bool `mapstructure:"enable"`
Bootstrap bool `mapstructure:"bootstrap"`
Channel string `mapstructure:"channel"`
Features map[string]map[string]string `mapstructure:"features"`
Enable bool `mapstructure:"enable"`
Bootstrap bool `mapstructure:"bootstrap"`
Channel string `mapstructure:"channel"`
Features map[string]map[string]string `mapstructure:"features"`
ModelDefaults map[string]string `mapstructure:"model-defaults"`
BootstrapConstraints map[string]string `mapstructure:"bootstrap-constraints"`
}

// hostConfig is a top-level field containing addition configuration for the host being
Expand Down
15 changes: 4 additions & 11 deletions internal/config/presets.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@ var defaultMicroK8sConfig microk8sConfig = microk8sConfig{

// defaultK8sConfig is the standard K8s config used throughout presets.
var defaultK8sConfig k8sConfig = k8sConfig{
Enable: true,
Bootstrap: true,
Enable: true,
Bootstrap: true,
BootstrapConstraints: map[string]string{"root-disk": "2G"},
Features: map[string]map[string]string{
"load-balancer": {
"l2-mode": "true",
Expand All @@ -86,15 +87,7 @@ var machinePreset *Config = &Config{
// k8sPreset is a configuration preset designed to be used when testing
// k8s charms.
var k8sPreset *Config = &Config{
Juju: jujuConfig{
ModelDefaults: map[string]string{
"test-mode": "true",
"automatically-retry-hooks": "false",
},
BootstrapConstraints: map[string]string{
"root-disk": "2G",
},
},
Juju: defaultJujuConfig,
Providers: providerConfig{
// Enable LXD so charms can be built, but don't bootstrap onto it.
LXD: lxdConfig{Enable: true},
Expand Down
25 changes: 21 additions & 4 deletions internal/juju/juju.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,18 @@ func (j *JujuHandler) bootstrapProvider(provider providers.Provider) error {
"--verbose",
}

// Combine the global and provider-local model-defaults and bootstrap-constraints.
modelDefaults := mergeMaps(j.modelDefaults, provider.ModelDefaults())
bootstrapConstraints := mergeMaps(j.bootstrapConstraints, provider.BootstrapConstraints())

// Iterate over the model-defaults and append them to the bootstrapArgs
for _, k := range sortedKeys(j.modelDefaults) {
bootstrapArgs = append(bootstrapArgs, "--model-default", fmt.Sprintf("%s=%s", k, j.modelDefaults[k]))
for _, k := range sortedKeys(modelDefaults) {
bootstrapArgs = append(bootstrapArgs, "--model-default", fmt.Sprintf("%s=%s", k, modelDefaults[k]))
}

// Iterate over the bootstrap-constraints and append them to the bootstrapArgs
for _, k := range sortedKeys(j.bootstrapConstraints) {
bootstrapArgs = append(bootstrapArgs, "--bootstrap-constraints", fmt.Sprintf("%s=%s", k, j.bootstrapConstraints[k]))
for _, k := range sortedKeys(bootstrapConstraints) {
bootstrapArgs = append(bootstrapArgs, "--bootstrap-constraints", fmt.Sprintf("%s=%s", k, bootstrapConstraints[k]))
}

user := j.system.User().Username
Expand Down Expand Up @@ -279,3 +283,16 @@ func sortedKeys(m map[string]string) []string {
slices.Sort(keys)
return keys
}

// mergeMaps takes two maps and returns a combined map, where KV pairs in the second map arg
// take precedence over the first.
func mergeMaps(m1 map[string]string, m2 map[string]string) map[string]string {
combinedMap := map[string]string{}
for k := range m1 {
combinedMap[k] = m1[k]
}
for k := range m2 {
combinedMap[k] = m2[k]
}
return combinedMap
}
38 changes: 38 additions & 0 deletions internal/juju/juju_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,41 @@ func TestJujuRestoreKillController(t *testing.T) {
t.Fatalf("expected: %v, got: %v", expectedCommands, system.ExecutedCommands)
}
}

func TestJujuMapMerge(t *testing.T) {
type test struct {
m1 map[string]string
m2 map[string]string
expected map[string]string
}

tests := []test{
{
m1: map[string]string{"foo": "bar", "baz": "qux"},
m2: map[string]string{"foo": "baz"},
expected: map[string]string{"foo": "baz", "baz": "qux"},
},
{
m1: map[string]string{},
m2: map[string]string{"foo": "baz"},
expected: map[string]string{"foo": "baz"},
},
{
m1: map[string]string{"foo": "baz"},
m2: map[string]string{},
expected: map[string]string{"foo": "baz"},
},
{
m1: map[string]string{"foo": "baz"},
m2: map[string]string{"baz": "qux"},
expected: map[string]string{"foo": "baz", "baz": "qux"},
},
}

for _, tc := range tests {
merged := mergeMaps(tc.m1, tc.m2)
if !reflect.DeepEqual(tc.expected, merged) {
t.Fatalf("expected: %v, got: %v", tc.expected, merged)
}
}
}
32 changes: 20 additions & 12 deletions internal/providers/google.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,23 @@ func NewGoogle(system system.Worker, config *config.Config) *Google {
}

return &Google{
system: system,
bootstrap: config.Providers.Google.Bootstrap,
credentialsFile: credentialsFile,
credentials: map[string]interface{}{},
system: system,
bootstrap: config.Providers.Google.Bootstrap,
credentialsFile: credentialsFile,
credentials: map[string]interface{}{},
modelDefaults: config.Providers.Google.ModelDefaults,
bootstrapConstraints: config.Providers.Google.BootstrapConstraints,
}
}

// Google represents a Google cloud to bootstrap.
type Google struct {
bootstrap bool
system system.Worker
credentialsFile string
credentials map[string]interface{}
bootstrap bool
system system.Worker
credentialsFile string
credentials map[string]interface{}
modelDefaults map[string]string
bootstrapConstraints map[string]string
}

// Prepare installs and configures Google such that it can work in testing environments.
Expand Down Expand Up @@ -66,10 +70,14 @@ func (l *Google) CloudName() string { return "google" }
// GroupName reports the name of the POSIX group with permissions over the Google socket.
func (l *Google) GroupName() string { return "" }

// Credentials reports the section of Juju's credentials.yaml for the provider
func (l *Google) Credentials() map[string]interface{} {
return l.credentials
}
// Credentials reports the section of Juju's credentials.yaml for the provider.
func (l *Google) Credentials() map[string]interface{} { return l.credentials }

// ModelDefaults reports the Juju model-defaults specific to the provider.
func (l *Google) ModelDefaults() map[string]string { return l.modelDefaults }

// BootstrapConstraints reports the Juju bootstrap-constraints specific to the provider.
func (l *Google) BootstrapConstraints() map[string]string { return l.bootstrapConstraints }

// Remove Google provider.
func (l *Google) Restore() error {
Expand Down
25 changes: 18 additions & 7 deletions internal/providers/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ func NewK8s(r system.Worker, config *config.Config) *K8s {
}

return &K8s{
Channel: channel,
Features: config.Providers.K8s.Features,
bootstrap: config.Providers.K8s.Bootstrap,
system: r,
Channel: channel,
Features: config.Providers.K8s.Features,
bootstrap: config.Providers.K8s.Bootstrap,
modelDefaults: config.Providers.K8s.ModelDefaults,
bootstrapConstraints: config.Providers.K8s.BootstrapConstraints,
system: r,
snaps: []*system.Snap{
{Name: "k8s", Channel: channel},
{Name: "kubectl", Channel: "stable"},
Expand All @@ -43,9 +45,12 @@ type K8s struct {
Channel string
Features map[string]map[string]string

bootstrap bool
system system.Worker
snaps []*system.Snap
bootstrap bool
modelDefaults map[string]string
bootstrapConstraints map[string]string

system system.Worker
snaps []*system.Snap
}

// Prepare installs and configures K8s such that it can work in testing environments.
Expand Down Expand Up @@ -92,6 +97,12 @@ func (k *K8s) GroupName() string { return "" }
// Credentials reports the section of Juju's credentials.yaml for the provider
func (m K8s) Credentials() map[string]interface{} { return nil }

// ModelDefaults reports the Juju model-defaults specific to the provider.
func (m *K8s) ModelDefaults() map[string]string { return m.modelDefaults }

// BootstrapConstraints reports the Juju bootstrap-constraints specific to the provider.
func (m *K8s) BootstrapConstraints() map[string]string { return m.bootstrapConstraints }

// Remove uninstalls K8s and kubectl.
func (k *K8s) Restore() error {
snapHandler := packages.NewSnapHandler(k.system, k.snaps)
Expand Down
25 changes: 18 additions & 7 deletions internal/providers/lxd.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,25 @@ func NewLXD(r system.Worker, config *config.Config) *LXD {
}

return &LXD{
Channel: channel,
system: r,
bootstrap: config.Providers.LXD.Bootstrap,
snaps: []*system.Snap{{Name: "lxd", Channel: channel}},
Channel: channel,
system: r,
bootstrap: config.Providers.LXD.Bootstrap,
modelDefaults: config.Providers.LXD.ModelDefaults,
bootstrapConstraints: config.Providers.LXD.BootstrapConstraints,
snaps: []*system.Snap{{Name: "lxd", Channel: channel}},
}
}

// LXD represents a LXD install on a given machine.
type LXD struct {
Channel string

bootstrap bool
system system.Worker
snaps []*system.Snap
bootstrap bool
modelDefaults map[string]string
bootstrapConstraints map[string]string

system system.Worker
snaps []*system.Snap
}

// Prepare installs and configures LXD such that it can work in testing environments.
Expand Down Expand Up @@ -78,6 +83,12 @@ func (l *LXD) GroupName() string { return "lxd" }
// Credentials reports the section of Juju's credentials.yaml for the provider
func (l *LXD) Credentials() map[string]interface{} { return nil }

// ModelDefaults reports the Juju model-defaults specific to the provider.
func (l *LXD) ModelDefaults() map[string]string { return l.modelDefaults }

// BootstrapConstraints reports the Juju bootstrap-constraints specific to the provider.
func (l *LXD) BootstrapConstraints() map[string]string { return l.bootstrapConstraints }

// Remove uninstalls LXD.
func (l *LXD) Restore() error {
snapHandler := packages.NewSnapHandler(l.system, l.snaps)
Expand Down
Loading

0 comments on commit 864293b

Please sign in to comment.