[FEATURE] Config file compatible with Kustomize (#945)

pull/946/head^2
Erik Godding Boye 3 years ago committed by GitHub
parent 08bf145e95
commit 9a2a3ec0ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      docs/usage/configfile.md
  2. 3
      docs/usage/registries.md
  3. 8
      pkg/config/config_test.go
  4. 2
      pkg/config/jsonschema_test.go
  5. 97
      pkg/config/migrate_test.go
  6. 3
      pkg/config/test_assets/config_test_registries.yaml
  7. 3
      pkg/config/test_assets/config_test_simple.yaml
  8. 3
      pkg/config/test_assets/config_test_simple_2.yaml
  9. 3
      pkg/config/test_assets/config_test_simple_invalid_servers.yaml
  10. 56
      pkg/config/test_assets/config_test_simple_migration_v1alpha4.yaml
  11. 5
      pkg/config/types/types.go
  12. 44
      pkg/config/v1alpha4/migrations.go
  13. 16
      pkg/config/v1alpha4/schema.json
  14. 30
      pkg/config/v1alpha4/types.go
  15. 3
      tests/assets/config_env.yaml
  16. 3
      tests/assets/config_test_simple.yaml

@ -52,7 +52,8 @@ Since the config options and the config file are changing quite a bit, it's hard
# k3d configuration file, saved as e.g. /home/me/myk3dcluster.yaml
apiVersion: k3d.io/v1alpha4 # this will change in the future as we make everything more stable
kind: Simple # internally, we also have a Cluster config, which is not yet available externally
name: mycluster # name that you want to give to your cluster (will still be prefixed with `k3d-`)
metadata:
name: mycluster # name that you want to give to your cluster (will still be prefixed with `k3d-`)
servers: 1 # same as `--servers 1`
agents: 2 # same as `--agents 2`
kubeAPI: # same as `--api-port myhost.my.domain:6445` (where the name would resolve to 127.0.0.1)

@ -25,7 +25,8 @@ If you're using a `SimpleConfig` file to configure your k3d cluster, you may as
```yaml
apiVersion: k3d.io/v1alpha4
kind: Simple
name: test
metadata:
name: test
servers: 1
agents: 2
registries:

@ -44,7 +44,9 @@ func TestReadSimpleConfig(t *testing.T) {
APIVersion: "k3d.io/v1alpha4",
Kind: "Simple",
},
Name: "test",
ObjectMeta: configtypes.ObjectMeta{
Name: "test",
},
Servers: 1,
Agents: 2,
ExposeAPI: exposedAPI,
@ -268,7 +270,9 @@ func TestReadSimpleConfigRegistries(t *testing.T) {
APIVersion: "k3d.io/v1alpha4",
Kind: "Simple",
},
Name: "test",
ObjectMeta: configtypes.ObjectMeta{
Name: "test",
},
Servers: 1,
Agents: 1,
Registries: conf.SimpleConfigRegistries{

@ -46,7 +46,7 @@ func TestValidateSchemaFail(t *testing.T) {
t.Errorf("Validation of config file %s against the default schema passed where we expected a failure", cfgPath)
}
expectedErrorText := `- name: Invalid type. Expected: string, given: integer
expectedErrorText := `- metadata.name: Invalid type. Expected: string, given: integer
`
if err.Error() != expectedErrorText {

@ -26,59 +26,76 @@ import (
"testing"
"github.com/go-test/deep"
"github.com/rancher/k3d/v5/pkg/config/v1alpha3"
"github.com/rancher/k3d/v5/pkg/config/v1alpha4"
l "github.com/rancher/k3d/v5/pkg/logger"
"github.com/spf13/viper"
)
func TestMigrateV1Alpha2ToV1Alpha3(t *testing.T) {
actualPath := "test_assets/config_test_simple_migration_v1alpha2.yaml"
expectedPath := "test_assets/config_test_simple_migration_v1alpha3.yaml"
actualViper := viper.New()
expectedViper := viper.New()
func TestMigrate(t *testing.T) {
tests := map[string]struct {
targetVersion string
actualPath string
expectedPath string
}{
"V1Alpha2ToV1Alpha3": {
targetVersion: v1alpha3.ApiVersion,
actualPath: "test_assets/config_test_simple_migration_v1alpha2.yaml",
expectedPath: "test_assets/config_test_simple_migration_v1alpha3.yaml",
},
"V1Alpha2ToV1Alpha4": {
targetVersion: v1alpha4.ApiVersion,
actualPath: "test_assets/config_test_simple_migration_v1alpha2.yaml",
expectedPath: "test_assets/config_test_simple_migration_v1alpha4.yaml",
},
"V1Alpha3ToV1Alpha4": {
targetVersion: v1alpha4.ApiVersion,
actualPath: "test_assets/config_test_simple_migration_v1alpha3.yaml",
expectedPath: "test_assets/config_test_simple_migration_v1alpha4.yaml",
},
}
actualViper.SetConfigType("yaml")
expectedViper.SetConfigType("yaml")
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
actualViper.SetConfigFile(actualPath)
expectedViper.SetConfigFile(expectedPath)
actualViper := viper.New()
expectedViper := viper.New()
if err := actualViper.ReadInConfig(); err != nil {
t.Fatal(err)
}
actualViper.SetConfigType("yaml")
expectedViper.SetConfigType("yaml")
if err := expectedViper.ReadInConfig(); err != nil {
t.Fatal(err)
}
actualViper.SetConfigFile(tc.actualPath)
expectedViper.SetConfigFile(tc.expectedPath)
actualCfg, err := FromViper(actualViper)
if err != nil {
t.Fatal(err)
}
if err := actualViper.ReadInConfig(); err != nil {
t.Fatal(err)
}
if actualCfg.GetAPIVersion() != DefaultConfigApiVersion {
actualCfg, err = Migrate(actualCfg, DefaultConfigApiVersion)
if err != nil {
l.Log().Fatalln(err)
}
}
if err := expectedViper.ReadInConfig(); err != nil {
t.Fatal(err)
}
expectedCfg, err := FromViper(expectedViper)
if err != nil {
t.Fatal(err)
}
actualCfg, err := FromViper(actualViper)
if err != nil {
t.Fatal(err)
}
if diff := deep.Equal(actualCfg, expectedCfg); diff != nil {
t.Fatalf("Actual\n%#v\ndoes not match expected\n%+v\nDiff:\n%#v", actualCfg, expectedCfg, diff)
}
if actualCfg.GetAPIVersion() != tc.targetVersion {
actualCfg, err = Migrate(actualCfg, tc.targetVersion)
if err != nil {
l.Log().Fatalln(err)
}
}
}
expectedCfg, err := FromViper(expectedViper)
if err != nil {
t.Fatal(err)
}
func TestMigrateV1Alpha2ToV1Alpha4(t *testing.T) {
t.Log("not implemented") // TODO: test migration v1alpha2 to v1alpha4
}
if diff := deep.Equal(actualCfg, expectedCfg); diff != nil {
t.Fatalf("Actual\n%#v\ndoes not match expected\n%+v\nDiff:\n%#v", actualCfg, expectedCfg, diff)
}
func TestMigrateV1Alpha3ToV1Alpha4(t *testing.T) {
t.Log("not implemented") // TODO: test migration v1alpha3 to v1alpha4
})
}
}

@ -1,6 +1,7 @@
apiVersion: k3d.io/v1alpha4
kind: Simple
name: test
metadata:
name: test
servers: 1
agents: 1
registries:

@ -1,6 +1,7 @@
apiVersion: k3d.io/v1alpha4
kind: Simple
name: test
metadata:
name: test
servers: 1
agents: 2
kubeAPI:

@ -1,4 +1,5 @@
apiVersion: k3d.io/v1alpha4
kind: Simple
name: supertest
metadata:
name: supertest
agents: 8

@ -1,6 +1,7 @@
apiVersion: k3d.io/v1alpha4
kind: Simple
name: 1234
metadata:
name: 1234
servers: 1
agents: 2
kubeAPI:

@ -0,0 +1,56 @@
apiVersion: k3d.io/v1alpha4
kind: Simple
metadata:
name: test
servers: 3
agents: 2
kubeAPI:
hostIP: "0.0.0.0"
hostPort: "6446"
#image: rancher/k3s:latest
volumes:
- volume: /my/path:/some/path
nodeFilters:
- all
ports:
- port: 80:80
nodeFilters:
- loadbalancer
- port: 0.0.0.0:443:443
nodeFilters:
- loadbalancer
env:
- envVar: bar=baz,bob
nodeFilters:
- all
registries:
create:
name: k3d-test-registry
host: "0.0.0.0"
hostPort: random
config: |
mirrors:
"my.company.registry":
endpoint:
- http://my.company.registry:5000
options:
k3d:
wait: true
timeout: "360s" # should be pretty high for multi-server clusters to allow for a proper startup routine
disableLoadbalancer: false
disableImageVolume: false
k3s:
extraArgs:
- arg: --tls-san=127.0.0.1
nodeFilters:
- server:*
kubeconfig:
updateDefaultKubeconfig: true
switchCurrentContext: true
runtime:
labels:
- label: foo=bar
nodeFilters:
- server:0
- loadbalancer

@ -27,6 +27,11 @@ type TypeMeta struct {
APIVersion string `mapstructure:"apiVersion,omitempty" yaml:"apiVersion,omitempty" json:"apiVersion,omitempty"`
}
// ObjectMeta got its name from the Kubernetes counterpart.
type ObjectMeta struct {
Name string `mapstructure:"name,omitempty" yaml:"name,omitempty" json:"name,omitempty"`
}
// Config interface.
type Config interface {
GetKind() string

@ -23,6 +23,7 @@ THE SOFTWARE.
package v1alpha4
import (
"encoding/json"
"fmt"
configtypes "github.com/rancher/k3d/v5/pkg/config/types"
@ -52,5 +53,48 @@ func MigrateV1Alpha2(input configtypes.Config) (configtypes.Config, error) {
func MigrateV1Alpha3(input configtypes.Config) (configtypes.Config, error) {
l.Log().Debugln("Migrating v1alpha3 to v1alpha4")
/*
* We're migrating matching fields between versions by marshalling to JSON and back
*/
injson, err := json.Marshal(input)
if err != nil {
return nil, err
}
/*
* Migrate config of `kind: Simple`
*/
if input.GetKind() == "Simple" {
cfgIntermediate := v1alpha3.SimpleConfig{}
if err := json.Unmarshal(injson, &cfgIntermediate); err != nil {
return nil, err
}
intermediateJSON, err := json.Marshal(cfgIntermediate)
if err != nil {
return nil, err
}
cfg := SimpleConfig{}
if err := json.Unmarshal(intermediateJSON, &cfg); err != nil {
return nil, err
}
cfg.Name = cfgIntermediate.Name
/*
* Finalizing
*/
cfg.APIVersion = ApiVersion
l.Log().Debugf("Migrated config: %+v", cfg)
return cfg, nil
}
l.Log().Debugf("No migration needed for %s#%s -> %s#%s", input.GetAPIVersion(), input.GetKind(), ApiVersion, input.GetKind())
return input, nil
}

@ -21,10 +21,16 @@
],
"default": "Simple"
},
"name": {
"description": "Name of the cluster (must be a valid hostname and will be prefixed with 'k3d-'). Example: 'mycluster'.",
"type": "string",
"format": "hostname"
"metadata": {
"type": "object",
"properties": {
"name": {
"description": "Name of the cluster (must be a valid hostname and will be prefixed with 'k3d-'). Example: 'mycluster'.",
"type": "string",
"format": "hostname"
}
},
"additionalProperties": false
},
"servers": {
"type": "number",
@ -220,7 +226,7 @@
"type": "string"
},
"hostPidMode": {
"type":"boolean",
"type": "boolean",
"default": false
},
"labels": {

@ -135,21 +135,21 @@ type SimpleConfigRegistries struct {
// SimpleConfig describes the toplevel k3d configuration file.
type SimpleConfig struct {
config.TypeMeta `mapstructure:",squash" yaml:",inline"`
Name string `mapstructure:"name" yaml:"name,omitempty" json:"name,omitempty"`
Servers int `mapstructure:"servers" yaml:"servers,omitempty" json:"servers,omitempty"` //nolint:lll // default 1
Agents int `mapstructure:"agents" yaml:"agents,omitempty" json:"agents,omitempty"` //nolint:lll // default 0
ExposeAPI SimpleExposureOpts `mapstructure:"kubeAPI" yaml:"kubeAPI,omitempty" json:"kubeAPI,omitempty"`
Image string `mapstructure:"image" yaml:"image,omitempty" json:"image,omitempty"`
Network string `mapstructure:"network" yaml:"network,omitempty" json:"network,omitempty"`
Subnet string `mapstructure:"subnet" yaml:"subnet,omitempty" json:"subnet,omitempty"`
ClusterToken string `mapstructure:"token" yaml:"clusterToken,omitempty" json:"clusterToken,omitempty"` // default: auto-generated
Volumes []VolumeWithNodeFilters `mapstructure:"volumes" yaml:"volumes,omitempty" json:"volumes,omitempty"`
Ports []PortWithNodeFilters `mapstructure:"ports" yaml:"ports,omitempty" json:"ports,omitempty"`
Options SimpleConfigOptions `mapstructure:"options" yaml:"options,omitempty" json:"options,omitempty"`
Env []EnvVarWithNodeFilters `mapstructure:"env" yaml:"env,omitempty" json:"env,omitempty"`
Registries SimpleConfigRegistries `mapstructure:"registries" yaml:"registries,omitempty" json:"registries,omitempty"`
HostAliases []k3d.HostAlias `mapstructure:"hostAliases" yaml:"hostAliases,omitempty" json:"hostAliases,omitempty"`
config.TypeMeta `mapstructure:",squash" yaml:",inline"`
config.ObjectMeta `mapstructure:"metadata" yaml:"metadata,omitempty" json:"metadata,omitempty"`
Servers int `mapstructure:"servers" yaml:"servers,omitempty" json:"servers,omitempty"` //nolint:lll // default 1
Agents int `mapstructure:"agents" yaml:"agents,omitempty" json:"agents,omitempty"` //nolint:lll // default 0
ExposeAPI SimpleExposureOpts `mapstructure:"kubeAPI" yaml:"kubeAPI,omitempty" json:"kubeAPI,omitempty"`
Image string `mapstructure:"image" yaml:"image,omitempty" json:"image,omitempty"`
Network string `mapstructure:"network" yaml:"network,omitempty" json:"network,omitempty"`
Subnet string `mapstructure:"subnet" yaml:"subnet,omitempty" json:"subnet,omitempty"`
ClusterToken string `mapstructure:"token" yaml:"clusterToken,omitempty" json:"clusterToken,omitempty"` // default: auto-generated
Volumes []VolumeWithNodeFilters `mapstructure:"volumes" yaml:"volumes,omitempty" json:"volumes,omitempty"`
Ports []PortWithNodeFilters `mapstructure:"ports" yaml:"ports,omitempty" json:"ports,omitempty"`
Options SimpleConfigOptions `mapstructure:"options" yaml:"options,omitempty" json:"options,omitempty"`
Env []EnvVarWithNodeFilters `mapstructure:"env" yaml:"env,omitempty" json:"env,omitempty"`
Registries SimpleConfigRegistries `mapstructure:"registries" yaml:"registries,omitempty" json:"registries,omitempty"`
HostAliases []k3d.HostAlias `mapstructure:"hostAliases" yaml:"hostAliases,omitempty" json:"hostAliases,omitempty"`
}
// SimpleExposureOpts provides a simplified syntax compared to the original k3d.ExposureOpts

@ -1,4 +1,5 @@
apiVersion: k3d.io/v1alpha4
kind: Simple
name: testenvexpand
metadata:
name: testenvexpand
servers: ${K3D_TEST_SERVERS}

@ -1,6 +1,7 @@
apiVersion: k3d.io/v1alpha4
kind: Simple
name: test
metadata:
name: test
servers: 3
agents: 2
#image: rancher/k3s:latest

Loading…
Cancel
Save