Add support for ulimits (#1264)

pull/1268/head
dan-ash 1 year ago committed by GitHub
parent 3457b2f9c4
commit 88c5f18792
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      cmd/cluster/clusterCreate.go
  2. 2
      cmd/cluster/clusterEdit.go
  3. 2
      cmd/config/configInit.go
  4. 38
      cmd/node/nodeCreate.go
  5. 110
      cmd/util/runtimeUlimits.go
  6. 12
      docs/usage/configfile.md
  7. 4
      docs/usage/registries.md
  8. 2
      pkg/client/cluster.go
  9. 2
      pkg/client/ports.go
  10. 8
      pkg/config/config.go
  11. 14
      pkg/config/config.versions.schema.json
  12. 15
      pkg/config/config_test.go
  13. 2
      pkg/config/jsonschema_test.go
  14. 2
      pkg/config/merge.go
  15. 2
      pkg/config/merge_test.go
  16. 7
      pkg/config/migrate_test.go
  17. 2
      pkg/config/process.go
  18. 2
      pkg/config/process_test.go
  19. 2
      pkg/config/test_assets/config_test_cluster.yaml
  20. 2
      pkg/config/test_assets/config_test_cluster_list.yaml
  21. 2
      pkg/config/test_assets/config_test_registries.yaml
  22. 6
      pkg/config/test_assets/config_test_simple.yaml
  23. 2
      pkg/config/test_assets/config_test_simple_2.yaml
  24. 2
      pkg/config/test_assets/config_test_simple_invalid_servers.yaml
  25. 56
      pkg/config/test_assets/config_test_simple_migration_v1alpha5.yaml
  26. 2
      pkg/config/test_assets/config_test_unknown.yaml
  27. 20
      pkg/config/transform.go
  28. 2
      pkg/config/transform_test.go
  29. 116
      pkg/config/v1alpha5/migrations.go
  30. 405
      pkg/config/v1alpha5/schema.json
  31. 229
      pkg/config/v1alpha5/types.go
  32. 2
      pkg/config/validate.go
  33. 2
      pkg/config/validate_test.go
  34. 5
      pkg/runtimes/docker/translate.go
  35. 46
      pkg/types/types.go
  36. 2
      tests/assets/config_env.yaml
  37. 2
      tests/assets/config_test_simple.yaml
  38. 4
      tests/test_config_file_from_stdin.sh
  39. 1
      tests/test_config_file_migration.sh

@ -36,11 +36,12 @@ import (
"inet.af/netaddr"
"sigs.k8s.io/yaml"
"github.com/k3d-io/k3d/v5/cmd/util"
cliutil "github.com/k3d-io/k3d/v5/cmd/util"
cliconfig "github.com/k3d-io/k3d/v5/cmd/util/config"
k3dCluster "github.com/k3d-io/k3d/v5/pkg/client"
"github.com/k3d-io/k3d/v5/pkg/config"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
l "github.com/k3d-io/k3d/v5/pkg/logger"
"github.com/k3d-io/k3d/v5/pkg/runtimes"
k3d "github.com/k3d-io/k3d/v5/pkg/types"
@ -251,6 +252,9 @@ func NewCmdClusterCreate() *cobra.Command {
cmd.Flags().StringArrayP("runtime-label", "", nil, "Add label to container runtime (Format: `KEY[=VALUE][@NODEFILTER[;NODEFILTER...]]`\n - Example: `k3d cluster create --agents 2 --runtime-label \"my.label@agent:0,1\" --runtime-label \"other.label=somevalue@server:0\"`")
_ = ppViper.BindPFlag("cli.runtime-labels", cmd.Flags().Lookup("runtime-label"))
cmd.Flags().StringArrayP("runtime-ulimit", "", nil, "Add ulimit to container runtime (Format: `NAME[=SOFT]:[HARD]`\n - Example: `k3d cluster create --agents 2 --runtime-ulimit \"nofile=1024:1024\" --runtime-ulimit \"noproc=1024:1024\"`")
_ = ppViper.BindPFlag("cli.runtime-ulimits", cmd.Flags().Lookup("runtime-ulimit"))
cmd.Flags().String("registry-create", "", "Create a k3d-managed registry and connect it to the cluster (Format: `NAME[:HOST][:HOSTPORT]`\n - Example: `k3d cluster create --registry-create mycluster-registry:0.0.0.0:5432`")
_ = ppViper.BindPFlag("cli.registries.create", cmd.Flags().Lookup("registry-create"))
@ -515,6 +519,10 @@ func applyCLIOverrides(cfg conf.SimpleConfig) (conf.SimpleConfig, error) {
l.Log().Tracef("RuntimeLabelFilterMap: %+v", runtimeLabelFilterMap)
for _, ulimit := range ppViper.GetStringSlice("cli.runtime-ulimits") {
cfg.Options.Runtime.Ulimits = append(cfg.Options.Runtime.Ulimits, *util.ParseRuntimeUlimit[conf.Ulimit](ulimit))
}
// --env
// envFilterMap will add container env vars to applied node filters
envFilterMap := make(map[string][]string, 1)

@ -25,7 +25,7 @@ import (
"github.com/k3d-io/k3d/v5/cmd/util"
cliutil "github.com/k3d-io/k3d/v5/cmd/util"
"github.com/k3d-io/k3d/v5/pkg/client"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
l "github.com/k3d-io/k3d/v5/pkg/logger"
"github.com/k3d-io/k3d/v5/pkg/runtimes"
k3d "github.com/k3d-io/k3d/v5/pkg/types"

@ -25,7 +25,7 @@ import (
"fmt"
"os"
config "github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
config "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
l "github.com/k3d-io/k3d/v5/pkg/logger"
"github.com/spf13/cobra"
)

@ -29,7 +29,6 @@ import (
"github.com/spf13/cobra"
dockerunits "github.com/docker/go-units"
"github.com/k3d-io/k3d/v5/cmd/util"
cliutil "github.com/k3d-io/k3d/v5/cmd/util"
k3dc "github.com/k3d-io/k3d/v5/pkg/client"
l "github.com/k3d-io/k3d/v5/pkg/logger"
@ -68,11 +67,11 @@ func NewCmdNodeCreate() *cobra.Command {
// add flags
cmd.Flags().Int("replicas", 1, "Number of replicas of this node specification.")
cmd.Flags().String("role", string(k3d.AgentRole), "Specify node role [server, agent]")
if err := cmd.RegisterFlagCompletionFunc("role", util.ValidArgsNodeRoles); err != nil {
if err := cmd.RegisterFlagCompletionFunc("role", cliutil.ValidArgsNodeRoles); err != nil {
l.Log().Fatalln("Failed to register flag completion for '--role'", err)
}
cmd.Flags().StringP("cluster", "c", k3d.DefaultClusterName, "Cluster URL or k3d cluster name to connect to.")
if err := cmd.RegisterFlagCompletionFunc("cluster", util.ValidArgsAvailableClusters); err != nil {
if err := cmd.RegisterFlagCompletionFunc("cluster", cliutil.ValidArgsAvailableClusters); err != nil {
l.Log().Fatalln("Failed to register flag completion for '--cluster'", err)
}
@ -83,6 +82,7 @@ func NewCmdNodeCreate() *cobra.Command {
cmd.Flags().DurationVar(&createNodeOpts.Timeout, "timeout", 0*time.Second, "Maximum waiting time for '--wait' before canceling/returning.")
cmd.Flags().StringSliceP("runtime-label", "", []string{}, "Specify container runtime labels in format \"foo=bar\"")
cmd.Flags().StringSliceP("runtime-ulimit", "", []string{}, "Specify container runtime ulimit in format \"ulimit=soft:hard\"")
cmd.Flags().StringSliceP("k3s-node-label", "", []string{}, "Specify k3s node labels in format \"foo=bar\"")
cmd.Flags().StringSliceP("network", "n", []string{}, "Add node to (another) runtime network")
@ -142,8 +142,7 @@ func parseCreateNodeCmd(cmd *cobra.Command, args []string) ([]*k3d.Node, string)
// --runtime-label
runtimeLabelsFlag, err := cmd.Flags().GetStringSlice("runtime-label")
if err != nil {
l.Log().Errorln("No runtime-label specified")
l.Log().Fatalln(err)
l.Log().Fatalf("No runtime-label specified: %v", err)
}
runtimeLabels := make(map[string]string, len(runtimeLabelsFlag)+1)
@ -159,6 +158,16 @@ func parseCreateNodeCmd(cmd *cobra.Command, args []string) ([]*k3d.Node, string)
// Internal k3d runtime labels take precedence over user-defined labels
runtimeLabels[k3d.LabelRole] = roleStr
// --runtime-ulimit
runtimeUlimitsFlag, err := cmd.Flags().GetStringSlice("runtime-ulimit")
if err != nil {
l.Log().Fatalf("No runtime-ulimit specified: %v", err)
}
runtimeUlimits := make([]*dockerunits.Ulimit, len(runtimeUlimitsFlag))
for index, ulimit := range runtimeUlimitsFlag {
runtimeUlimits[index] = cliutil.ParseRuntimeUlimit[dockerunits.Ulimit](ulimit)
}
// --k3s-node-label
k3sNodeLabelsFlag, err := cmd.Flags().GetStringSlice("k3s-node-label")
if err != nil {
@ -191,15 +200,16 @@ func parseCreateNodeCmd(cmd *cobra.Command, args []string) ([]*k3d.Node, string)
nodes := []*k3d.Node{}
for i := 0; i < replicas; i++ {
node := &k3d.Node{
Name: fmt.Sprintf("%s-%s-%d", k3d.DefaultObjectNamePrefix, args[0], i),
Role: role,
Image: image,
K3sNodeLabels: k3sNodeLabels,
RuntimeLabels: runtimeLabels,
Restart: true,
Memory: memory,
Networks: networks,
Args: k3sArgs,
Name: fmt.Sprintf("%s-%s-%d", k3d.DefaultObjectNamePrefix, args[0], i),
Role: role,
Image: image,
K3sNodeLabels: k3sNodeLabels,
RuntimeLabels: runtimeLabels,
RuntimeUlimits: runtimeUlimits,
Restart: true,
Memory: memory,
Networks: networks,
Args: k3sArgs,
}
nodes = append(nodes, node)
}

@ -0,0 +1,110 @@
/*
Copyright © 2020-2022 The k3d Author(s)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package util
import (
"strconv"
"strings"
dockerunits "github.com/docker/go-units"
"github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
l "github.com/k3d-io/k3d/v5/pkg/logger"
)
type UlimitTypes interface {
dockerunits.Ulimit | v1alpha5.Ulimit
}
type Ulimit[T UlimitTypes] struct {
Values T
}
// ValidateRuntimeUlimitKey validates a given ulimit key is valid
func ValidateRuntimeUlimitKey(ulimitKey string) {
ulimitsKeys := map[string]bool{
"core": true,
"cpu": true,
"data": true,
"fsize": true,
"locks": true,
"memlock": true,
"msgqueue": true,
"nice": true,
"nofile": true,
"nproc": true,
"rss": true,
"rtprio": true,
"rttime": true,
"sigpending": true,
"stack": true,
}
keysList := make([]string, 0, len(ulimitsKeys))
for key := range ulimitsKeys {
keysList = append(keysList, key)
}
if !ulimitsKeys[ulimitKey] {
l.Log().Fatalf("runtime ulimit \"%s\" is not valid, allowed keys are: %s", ulimitKey, strings.Join(keysList, ", "))
}
}
func ParseRuntimeUlimit[T UlimitTypes](ulimit string) *T {
var parsedUlimit any
var tmpUlimit Ulimit[T]
ulimitSplitted := strings.Split(ulimit, "=")
if len(ulimitSplitted) != 2 {
l.Log().Fatalf("unknown runtime-ulimit format format: %s, use format \"ulimit=soft:hard\"", ulimit)
}
ValidateRuntimeUlimitKey(ulimitSplitted[0])
softHardSplitted := strings.Split(ulimitSplitted[1], ":")
if len(softHardSplitted) != 2 {
l.Log().Fatalf("unknown runtime-ulimit format format: %s, use format \"ulimit=soft:hard\"", ulimit)
}
soft, err := strconv.Atoi(softHardSplitted[0])
if err != nil {
l.Log().Fatalf("unknown runtime-ulimit format format: soft %s has to be int", ulimitSplitted[0])
}
hard, err := strconv.Atoi(softHardSplitted[1])
if err != nil {
l.Log().Fatalf("unknown runtime-ulimit format format: hard %s has to be int", ulimitSplitted[1])
}
switch any(tmpUlimit.Values).(type) {
case dockerunits.Ulimit:
parsedUlimit = &dockerunits.Ulimit{
Name: ulimitSplitted[0],
Soft: int64(soft),
Hard: int64(hard),
}
case v1alpha5.Ulimit:
parsedUlimit = &v1alpha5.Ulimit{
Name: ulimitSplitted[0],
Soft: int64(soft),
Hard: int64(hard),
}
default:
l.Log().Fatalf("Unsupported UlimitTypes, supported types are: dockerunits.Ulimit or v1alpha5.Ulimit")
}
return parsedUlimit.(*T)
}

@ -24,13 +24,13 @@ Using a config file is as easy as putting it in a well-known place in your file
As of the time of writing this documentation, the config file only **requires** you to define two fields:
- `apiVersion` to match the version of the config file that you want to use (at this time it would be `apiVersion: k3d.io/v1alpha4`)
- `apiVersion` to match the version of the config file that you want to use (at this time it would be `apiVersion: k3d.io/v1alpha5`)
- `kind` to define the kind of config file that you want to use (currently we only have the `Simple` config)
So this would be the minimal config file, which configures absolutely nothing:
```yaml
apiVersion: k3d.io/v1alpha4
apiVersion: k3d.io/v1alpha5
kind: Simple
```
@ -42,7 +42,7 @@ Currently, the config file is still in an Alpha-State, meaning, that it is subje
!!! info "Validation via JSON-Schema"
k3d uses a [JSON-Schema](https://json-schema.org/) to describe the expected format and fields of the configuration file.
This schema is also used to [validate](https://github.com/xeipuuv/gojsonschema#validation) a user-given config file.
This JSON-Schema can be found in the specific config version sub-directory in the repository (e.g. [here for `v1alpha4`](https://github.com/k3d-io/k3d/blob/main/pkg/config/v1alpha4/schema.json)) and could be used to lookup supported fields or by linters to validate the config file, e.g. in your code editor.
This JSON-Schema can be found in the specific config version sub-directory in the repository (e.g. [here for `v1alpha5`](https://github.com/k3d-io/k3d/blob/main/pkg/config/v1alpha5/schema.json)) and could be used to lookup supported fields or by linters to validate the config file, e.g. in your code editor.
### All Options: Example
@ -50,7 +50,7 @@ Since the config options and the config file are changing quite a bit, it's hard
```yaml
# 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
apiVersion: k3d.io/v1alpha5 # 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
metadata:
name: mycluster # name that you want to give to your cluster (will still be prefixed with `k3d-`)
@ -131,6 +131,10 @@ options:
- label: bar=baz # same as `--runtime-label 'bar=baz@agent:1'` -> this results in a runtime (docker) container label
nodeFilters:
- agent:1
ulimits:
- name: nofile
soft: 26677
hard: 26677
```

@ -23,7 +23,7 @@ This file can also be used for providing additional information necessary for ac
If you're using a `SimpleConfig` file to configure your k3d cluster, you may as well embed the registries.yaml in there directly:
```yaml
apiVersion: k3d.io/v1alpha4
apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
name: test
@ -252,7 +252,7 @@ Then you should check that the pod is running with `kubectl get pods -l "app=ngi
1. Create a config file, e.g. `/home/me/test-regcache.yaml`
```yaml
apiVersion: k3d.io/v1alpha4
apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
name: test-regcache

@ -42,7 +42,7 @@ import (
"sigs.k8s.io/yaml"
"github.com/k3d-io/k3d/v5/pkg/actions"
config "github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
config "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
l "github.com/k3d-io/k3d/v5/pkg/logger"
"github.com/k3d-io/k3d/v5/pkg/runtimes"
k3drt "github.com/k3d-io/k3d/v5/pkg/runtimes"

@ -32,7 +32,7 @@ import (
"sigs.k8s.io/yaml"
"github.com/k3d-io/k3d/v5/pkg/config/types"
config "github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
config "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
l "github.com/k3d-io/k3d/v5/pkg/logger"
"github.com/k3d-io/k3d/v5/pkg/runtimes"
k3d "github.com/k3d-io/k3d/v5/pkg/types"

@ -32,7 +32,8 @@ import (
"github.com/k3d-io/k3d/v5/pkg/config/v1alpha2"
"github.com/k3d-io/k3d/v5/pkg/config/v1alpha3"
"github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
defaultConfig "github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
"github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
defaultConfig "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
types "github.com/k3d-io/k3d/v5/pkg/config/types"
)
@ -43,6 +44,7 @@ var Schemas = map[string]string{
v1alpha2.ApiVersion: v1alpha2.JSONSchema,
v1alpha3.ApiVersion: v1alpha3.JSONSchema,
v1alpha4.ApiVersion: v1alpha4.JSONSchema,
v1alpha5.ApiVersion: v1alpha5.JSONSchema,
}
func GetSchemaByVersion(apiVersion string) ([]byte, error) {
@ -70,6 +72,8 @@ func FromViper(config *viper.Viper) (types.Config, error) {
cfg, err = v1alpha3.GetConfigByKind(kind)
case "k3d.io/v1alpha4":
cfg, err = v1alpha4.GetConfigByKind(kind)
case "k3d.io/v1alpha5":
cfg, err = v1alpha5.GetConfigByKind(kind)
case "":
cfg, err = defaultConfig.GetConfigByKind(kind)
default:
@ -93,6 +97,8 @@ func getMigrations(version string) map[string]func(types.Config) (types.Config,
return v1alpha3.Migrations
case v1alpha4.ApiVersion:
return v1alpha4.Migrations
case v1alpha5.ApiVersion:
return v1alpha5.Migrations
default:
return nil
}

@ -4,6 +4,20 @@
"title": "All k3d config versions",
"type": "object",
"oneOf": [
{
"allOf": [
{
"properties": {
"version": {
"const": "v1alpha5"
}
}
},
{
"$ref": "https://raw.githubusercontent.com/k3d-io/k3d/main/pkg/config/v1alpha5/schema.json"
}
]
},
{
"allOf": [
{

@ -27,7 +27,7 @@ import (
"github.com/go-test/deep"
configtypes "github.com/k3d-io/k3d/v5/pkg/config/types"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
"github.com/spf13/viper"
k3d "github.com/k3d-io/k3d/v5/pkg/types"
@ -41,7 +41,7 @@ func TestReadSimpleConfig(t *testing.T) {
expectedConfig := conf.SimpleConfig{
TypeMeta: configtypes.TypeMeta{
APIVersion: "k3d.io/v1alpha4",
APIVersion: "k3d.io/v1alpha5",
Kind: "Simple",
},
ObjectMeta: configtypes.ObjectMeta{
@ -104,6 +104,11 @@ func TestReadSimpleConfig(t *testing.T) {
NodeFilters: []string{"server:0", "loadbalancer"},
},
},
Ulimits: []conf.Ulimit{{
Name: "nofile",
Soft: 1024,
Hard: 1024,
}},
},
},
}
@ -139,7 +144,7 @@ func TestReadClusterConfig(t *testing.T) {
expectedConfig := conf.ClusterConfig{
TypeMeta: configtypes.TypeMeta{
APIVersion: "k3d.io/v1alpha4",
APIVersion: "k3d.io/v1alpha5",
Kind: "Cluster",
},
Cluster: k3d.Cluster{
@ -184,7 +189,7 @@ func TestReadClusterListConfig(t *testing.T) {
expectedConfig := conf.ClusterListConfig{
TypeMeta: configtypes.TypeMeta{
APIVersion: "k3d.io/v1alpha4",
APIVersion: "k3d.io/v1alpha5",
Kind: "ClusterList",
},
Clusters: []k3d.Cluster{
@ -267,7 +272,7 @@ func TestReadSimpleConfigRegistries(t *testing.T) {
expectedConfig := conf.SimpleConfig{
TypeMeta: configtypes.TypeMeta{
APIVersion: "k3d.io/v1alpha4",
APIVersion: "k3d.io/v1alpha5",
Kind: "Simple",
},
ObjectMeta: configtypes.ObjectMeta{

@ -24,7 +24,7 @@ package config
import (
"testing"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
)
func TestValidateSchema(t *testing.T) {

@ -26,7 +26,7 @@ import (
"fmt"
"github.com/imdario/mergo"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
l "github.com/k3d-io/k3d/v5/pkg/logger"
)

@ -26,7 +26,7 @@ import (
"testing"
configtypes "github.com/k3d-io/k3d/v5/pkg/config/types"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
"github.com/spf13/viper"
"gotest.tools/assert"
)

@ -28,6 +28,7 @@ import (
"github.com/go-test/deep"
"github.com/k3d-io/k3d/v5/pkg/config/v1alpha3"
"github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
"github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
l "github.com/k3d-io/k3d/v5/pkg/logger"
"github.com/spf13/viper"
)
@ -53,6 +54,11 @@ func TestMigrate(t *testing.T) {
actualPath: "test_assets/config_test_simple_migration_v1alpha3.yaml",
expectedPath: "test_assets/config_test_simple_migration_v1alpha4.yaml",
},
"V1Alpha4ToV1Alpha5": {
targetVersion: v1alpha5.ApiVersion,
actualPath: "test_assets/config_test_simple_migration_v1alpha4.yaml",
expectedPath: "test_assets/config_test_simple_migration_v1alpha5.yaml",
},
}
for name, tc := range tests {
@ -80,6 +86,7 @@ func TestMigrate(t *testing.T) {
t.Fatal(err)
}
t.Logf("Migrating %s to %s", actualCfg.GetAPIVersion(), tc.targetVersion)
if actualCfg.GetAPIVersion() != tc.targetVersion {
actualCfg, err = Migrate(actualCfg, tc.targetVersion)
if err != nil {

@ -25,7 +25,7 @@ package config
import (
"strings"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
l "github.com/k3d-io/k3d/v5/pkg/logger"
runtimeutil "github.com/k3d-io/k3d/v5/pkg/runtimes/util"
k3d "github.com/k3d-io/k3d/v5/pkg/types"

@ -27,7 +27,7 @@ import (
"strings"
"testing"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
"github.com/k3d-io/k3d/v5/pkg/runtimes"
"github.com/k3d-io/k3d/v5/pkg/types/k3s"
"github.com/spf13/viper"

@ -1,4 +1,4 @@
apiVersion: k3d.io/v1alpha4
apiVersion: k3d.io/v1alpha5
kind: Cluster
name: foo
nodes:

@ -1,5 +1,5 @@
---
apiVersion: k3d.io/v1alpha4
apiVersion: k3d.io/v1alpha5
kind: ClusterList
clusters:
- name: foo

@ -1,4 +1,4 @@
apiVersion: k3d.io/v1alpha4
apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
name: test

@ -1,4 +1,4 @@
apiVersion: k3d.io/v1alpha4
apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
name: test
@ -49,3 +49,7 @@ options:
nodeFilters:
- server:0
- loadbalancer
ulimits:
- name: nofile
soft: 1024
hard: 1024

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

@ -1,4 +1,4 @@
apiVersion: k3d.io/v1alpha4
apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
name: 1234

@ -0,0 +1,56 @@
apiVersion: k3d.io/v1alpha5
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

@ -1,3 +1,3 @@
apiVersion: k3d.io/v1alpha4
apiVersion: k3d.io/v1alpha5
kind: Unknown
foo: bar

@ -33,9 +33,10 @@ import (
"inet.af/netaddr"
"sigs.k8s.io/yaml"
dockerunits "github.com/docker/go-units"
cliutil "github.com/k3d-io/k3d/v5/cmd/util" // TODO: move parseapiport to pkg
"github.com/k3d-io/k3d/v5/pkg/client"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
l "github.com/k3d-io/k3d/v5/pkg/logger"
"github.com/k3d-io/k3d/v5/pkg/runtimes"
k3d "github.com/k3d-io/k3d/v5/pkg/types"
@ -235,6 +236,23 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
}
}
// -> RUNTIME ULIMITS
for index, runtimeUlimit := range simpleConfig.Options.Runtime.Ulimits {
for _, node := range nodeList {
if node.RuntimeUlimits == nil {
node.RuntimeUlimits = make([]*dockerunits.Ulimit, len(simpleConfig.Options.Runtime.Ulimits)) // ensure that the map is initialized
}
cliutil.ValidateRuntimeUlimitKey(runtimeUlimit.Name)
node.RuntimeUlimits[index] = &dockerunits.Ulimit{
Name: runtimeUlimit.Name,
Soft: runtimeUlimit.Soft,
Hard: runtimeUlimit.Hard,
}
}
}
// -> ENV
for _, envVarWithNodeFilters := range simpleConfig.Env {
if len(envVarWithNodeFilters.NodeFilters) == 0 && nodeCount > 1 {

@ -26,7 +26,7 @@ import (
"context"
"testing"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
"github.com/k3d-io/k3d/v5/pkg/runtimes"
"github.com/spf13/viper"
)

@ -0,0 +1,116 @@
/*
Copyright © 2020-2022 The k3d Author(s)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package v1alpha5
import (
"encoding/json"
"fmt"
configtypes "github.com/k3d-io/k3d/v5/pkg/config/types"
"github.com/k3d-io/k3d/v5/pkg/config/v1alpha2"
"github.com/k3d-io/k3d/v5/pkg/config/v1alpha3"
"github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
l "github.com/k3d-io/k3d/v5/pkg/logger"
)
var Migrations = map[string]func(configtypes.Config) (configtypes.Config, error){
v1alpha2.ApiVersion: MigrateV1Alpha2,
v1alpha3.ApiVersion: MigrateV1Alpha3,
v1alpha4.ApiVersion: MigrateV1Alpha4,
}
func MigrateV1Alpha2(input configtypes.Config) (configtypes.Config, error) {
l.Log().Debugln("Migrating v1alpha2 to v1alpha3")
// first, migrate to v1alpha3
input, err := v1alpha3.MigrateV1Alpha2(input)
if err != nil {
return nil, fmt.Errorf("error migration v1alpha2 to v1alpha3: %w", err)
}
// then, migrate to v1alpha4
input, err = v1alpha4.MigrateV1Alpha3(input)
if err != nil {
return nil, fmt.Errorf("error migration v1alpha3 to v1alpha4: %w", err)
}
// then, migrate to v1alpha5
return MigrateV1Alpha4(input)
}
func MigrateV1Alpha3(input configtypes.Config) (configtypes.Config, error) {
l.Log().Debugln("Migrating v1alpha3 to v1alpha4")
// first, migrate to v1alpha4
input, err := v1alpha4.MigrateV1Alpha3(input)
if err != nil {
return nil, fmt.Errorf("error migration v1alpha3 to v1alpha4: %w", err)
}
// then, migrate to v1alpha5
return MigrateV1Alpha4(input)
}
func MigrateV1Alpha4(input configtypes.Config) (configtypes.Config, error) {
l.Log().Debugln("Migrating v1alpha4 to v1alpha5")
/*
* 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 := v1alpha4.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
}

@ -0,0 +1,405 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "SimpleConfig",
"type": "object",
"required": [
"apiVersion",
"kind"
],
"properties": {
"apiVersion": {
"type": "string",
"enum": [
"k3d.io/v1alpha5"
],
"default": "k3d.io/v1alpha5"
},
"kind": {
"type": "string",
"enum": [
"Simple"
],
"default": "Simple"
},
"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",
"minimum": 1
},
"agents": {
"type": "number",
"minimum": 0
},
"kubeAPI": {
"type": "object",
"properties": {
"host": {
"type": "string",
"format": "hostname"
},
"hostIP": {
"type": "string",
"format": "ipv4",
"examples": [
"0.0.0.0",
"192.168.178.55"
]
},
"hostPort": {
"type": "string",
"examples": [
"6443"
]
}
},
"additionalProperties": false
},
"image": {
"type": "string",
"examples": [
"rancher/k3s:latest"
]
},
"network": {
"type": "string"
},
"subnet": {
"type": "string",
"default": "auto",
"examples": [
"172.28.0.0/16",
"192.162.0.0/16"
]
},
"token": {
"type": "string"
},
"volumes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"volume": {
"type": "string"
},
"nodeFilters": {
"$ref": "#/definitions/nodeFilters"
}
},
"additionalProperties": false
}
},
"ports": {
"type": "array",
"items": {
"type": "object",
"properties": {
"port": {
"type": "string"
},
"nodeFilters": {
"$ref": "#/definitions/nodeFilters"
}
},
"additionalProperties": false
}
},
"options": {
"type": "object",
"properties": {
"k3d": {
"type": "object",
"properties": {
"wait": {
"type": "boolean",
"default": true
},
"timeout": {
"examples": [
"60s",
"1m",
"1m30s"
]
},
"disableLoadbalancer": {
"type": "boolean",
"default": false
},
"disableImageVolume": {
"type": "boolean",
"default": false
},
"disableRollback": {
"type": "boolean",
"default": false
},
"loadbalancer": {
"type": "object",
"properties": {
"configOverrides": {
"type": "array",
"examples": [
"settings.workerConnections=2048",
"settings.defaultProxyTimeout=900"
]
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"k3s": {
"type": "object",
"properties": {
"extraArgs": {
"type": "array",
"items": {
"type": "object",
"properties": {
"arg": {
"type": "string",
"examples": [
"--tls-san=127.0.0.1",
"--disable=traefik"
]
},
"nodeFilters": {
"$ref": "#/definitions/nodeFilters"
}
},
"additionalProperties": false
}
},
"nodeLabels": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string"
},
"nodeFilters": {
"$ref": "#/definitions/nodeFilters"
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
},
"kubeconfig": {
"type": "object",
"properties": {
"updateDefaultKubeconfig": {
"type": "boolean",
"default": true
},
"switchCurrentContext": {
"type": "boolean",
"default": true
}
},
"additionalProperties": false
},
"runtime": {
"type": "object",
"properties": {
"gpuRequest": {
"type": "string"
},
"serversMemory": {
"type": "string"
},
"agentsMemory": {
"type": "string"
},
"hostPidMode": {
"type": "boolean",
"default": false
},
"labels": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string"
},
"nodeFilters": {
"$ref": "#/definitions/nodeFilters"
}
},
"additionalProperties": false
}
},
"ulimits": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"soft": {
"type": "number"
},
"hard": {
"type": "number"
}
},
"additionalProperties": false
}
}
}
}
},
"additionalProperties": false
},
"env": {
"type": "array",
"items": {
"type": "object",
"properties": {
"envVar": {
"type": "string"
},
"nodeFilters": {
"$ref": "#/definitions/nodeFilters"
}
},
"additionalProperties": false
}
},
"registries": {
"type": "object",
"properties": {
"create": {
"type": "object",
"description": "Create a new container image registry alongside the cluster.",
"properties": {
"name": {
"type": "string",
"examples": [
"myregistry",
"registry.localhost"
]
},
"host": {
"type": "string",
"examples": [
"0.0.0.0",
"localhost",
"127.0.0.1"
],
"default": "0.0.0.0"
},
"hostPort": {
"type": "string",
"examples": [
"5000",
"2345"
],
"default": "random"
},
"image": {
"type": "string",
"examples": [
"myregistry/registry:2"
],
"default": "docker.io/library/registry:2"
},
"proxy": {
"type": "object",
"properties": {
"remoteURL": {
"type": "string",
"examples": [
"https://registry-1.docker.io"
]
},
"username": {
"type": "string"
},
"password": {
"type": "string"
}
},
"additionalProperties": false
},
"volumes": {
"type": "array",
"items": {
"type": "string"
},
"examples": [
"/tmp/registry:/var/lib/registry"
]
}
},
"additionalProperties": false
},
"use": {
"type": "array",
"description": "Connect another container image registry to the cluster.",
"items": {
"type": "string"
},
"examples": [
"otherregistry:5000"
]
},
"config": {
"type": "string",
"description": "Reference a K3s registry configuration file or at it's contents here."
},
"additionalProperties": false
}
},
"hostAliases": {
"type": "array",
"description": "Additional IP to multiple hostnames mappings",
"items": {
"type": "object",
"properties": {
"ip": {
"type": "string"
},
"hostnames": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
},
"additionalProperties": false,
"definitions": {
"nodeFilters": {
"type": "array",
"items": {
"type": "string"
},
"examples": [
"loadbalancer",
"server:*",
"server:0",
"agent:1",
"all"
]
}
}
}

@ -0,0 +1,229 @@
/*
Copyright © 2020-2022 The k3d Author(s)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package v1alpha5
import (
_ "embed"
"fmt"
"strings"
"time"
config "github.com/k3d-io/k3d/v5/pkg/config/types"
k3d "github.com/k3d-io/k3d/v5/pkg/types"
"github.com/k3d-io/k3d/v5/version"
)
const ApiVersion = "k3d.io/v1alpha5"
// JSONSchema describes the schema used to validate config files
//go:embed schema.json
var JSONSchema string
// DefaultConfigTpl for printing
const DefaultConfigTpl = `---
apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
name: %s
servers: 1
agents: 0
image: %s
`
// DefaultConfig templated DefaultConfigTpl
var DefaultConfig = fmt.Sprintf(
DefaultConfigTpl,
k3d.DefaultClusterName,
fmt.Sprintf("%s:%s", k3d.DefaultK3sImageRepo, version.K3sVersion),
)
type VolumeWithNodeFilters struct {
Volume string `mapstructure:"volume" json:"volume,omitempty"`
NodeFilters []string `mapstructure:"nodeFilters" json:"nodeFilters,omitempty"`
}
type PortWithNodeFilters struct {
Port string `mapstructure:"port" json:"port,omitempty"`
NodeFilters []string `mapstructure:"nodeFilters" json:"nodeFilters,omitempty"`
}
type LabelWithNodeFilters struct {
Label string `mapstructure:"label" json:"label,omitempty"`
NodeFilters []string `mapstructure:"nodeFilters" json:"nodeFilters,omitempty"`
}
type EnvVarWithNodeFilters struct {
EnvVar string `mapstructure:"envVar" json:"envVar,omitempty"`
NodeFilters []string `mapstructure:"nodeFilters" json:"nodeFilters,omitempty"`
}
type K3sArgWithNodeFilters struct {
Arg string `mapstructure:"arg" json:"arg,omitempty"`
NodeFilters []string `mapstructure:"nodeFilters" json:"nodeFilters,omitempty"`
}
type SimpleConfigRegistryCreateConfig struct {
Name string `mapstructure:"name" json:"name,omitempty"`
Host string `mapstructure:"host" json:"host,omitempty"`
HostPort string `mapstructure:"hostPort" json:"hostPort,omitempty"`
Image string `mapstructure:"image" json:"image,omitempty"`
Proxy k3d.RegistryProxy `mapstructure:"proxy" json:"proxy,omitempty"`
Volumes []string `mapstructure:"volumes" json:"volumes,omitempty"`
}
// SimpleConfigOptionsKubeconfig describes the set of options referring to the kubeconfig during cluster creation.
type SimpleConfigOptionsKubeconfig struct {
UpdateDefaultKubeconfig bool `mapstructure:"updateDefaultKubeconfig" json:"updateDefaultKubeconfig,omitempty"` // default: true
SwitchCurrentContext bool `mapstructure:"switchCurrentContext" json:"switchCurrentContext,omitempty"` //nolint:lll // default: true
}
type SimpleConfigOptions struct {
K3dOptions SimpleConfigOptionsK3d `mapstructure:"k3d" json:"k3d"`
K3sOptions SimpleConfigOptionsK3s `mapstructure:"k3s" json:"k3s"`
KubeconfigOptions SimpleConfigOptionsKubeconfig `mapstructure:"kubeconfig" json:"kubeconfig"`
Runtime SimpleConfigOptionsRuntime `mapstructure:"runtime" json:"runtime"`
}
type SimpleConfigOptionsRuntime struct {
GPURequest string `mapstructure:"gpuRequest" json:"gpuRequest,omitempty"`
ServersMemory string `mapstructure:"serversMemory" json:"serversMemory,omitempty"`
AgentsMemory string `mapstructure:"agentsMemory" json:"agentsMemory,omitempty"`
HostPidMode bool `mapstructure:"hostPidMode" yjson:"hostPidMode,omitempty"`
Labels []LabelWithNodeFilters `mapstructure:"labels" json:"labels,omitempty"`
Ulimits []Ulimit `mapstructure:"ulimits" json:"ulimits,omitempty"`
}
type Ulimit struct {
Name string `mapstructure:"name" json:"name"`
Soft int64 `mapstructure:"soft" json:"soft"`
Hard int64 `mapstructure:"hard" json:"hard"`
}
type SimpleConfigOptionsK3d struct {
Wait bool `mapstructure:"wait" json:"wait"`
Timeout time.Duration `mapstructure:"timeout" json:"timeout,omitempty"`
DisableLoadbalancer bool `mapstructure:"disableLoadbalancer" json:"disableLoadbalancer"`
DisableImageVolume bool `mapstructure:"disableImageVolume" json:"disableImageVolume"`
NoRollback bool `mapstructure:"disableRollback" json:"disableRollback"`
NodeHookActions []k3d.NodeHookAction `mapstructure:"nodeHookActions" json:"nodeHookActions,omitempty"`
Loadbalancer SimpleConfigOptionsK3dLoadbalancer `mapstructure:"loadbalancer" json:"loadbalancer,omitempty"`
}
type SimpleConfigOptionsK3dLoadbalancer struct {
ConfigOverrides []string `mapstructure:"configOverrides" json:"configOverrides,omitempty"`
}
type SimpleConfigOptionsK3s struct {
ExtraArgs []K3sArgWithNodeFilters `mapstructure:"extraArgs" json:"extraArgs,omitempty"`
NodeLabels []LabelWithNodeFilters `mapstructure:"nodeLabels" json:"nodeLabels,omitempty"`
}
type SimpleConfigRegistries struct {
Use []string `mapstructure:"use" json:"use,omitempty"`
Create *SimpleConfigRegistryCreateConfig `mapstructure:"create" json:"create,omitempty"`
Config string `mapstructure:"config" json:"config,omitempty"` // registries.yaml (k3s config for containerd registry override)
}
// SimpleConfig describes the toplevel k3d configuration file.
type SimpleConfig struct {
config.TypeMeta `mapstructure:",squash"`
config.ObjectMeta `mapstructure:"metadata" json:"metadata,omitempty"`
Servers int `mapstructure:"servers" json:"servers,omitempty"` //nolint:lll // default 1
Agents int `mapstructure:"agents" json:"agents,omitempty"` //nolint:lll // default 0
ExposeAPI SimpleExposureOpts `mapstructure:"kubeAPI" json:"kubeAPI,omitempty"`
Image string `mapstructure:"image" json:"image,omitempty"`
Network string `mapstructure:"network" json:"network,omitempty"`
Subnet string `mapstructure:"subnet" json:"subnet,omitempty"`
ClusterToken string `mapstructure:"token" json:"clusterToken,omitempty"` // default: auto-generated
Volumes []VolumeWithNodeFilters `mapstructure:"volumes" json:"volumes,omitempty"`
Ports []PortWithNodeFilters `mapstructure:"ports" json:"ports,omitempty"`
Options SimpleConfigOptions `mapstructure:"options" json:"options,omitempty"`
Env []EnvVarWithNodeFilters `mapstructure:"env" json:"env,omitempty"`
Registries SimpleConfigRegistries `mapstructure:"registries" json:"registries,omitempty"`
HostAliases []k3d.HostAlias `mapstructure:"hostAliases" json:"hostAliases,omitempty"`
}
// SimpleExposureOpts provides a simplified syntax compared to the original k3d.ExposureOpts
type SimpleExposureOpts struct {
Host string `mapstructure:"host" json:"host,omitempty"`
HostIP string `mapstructure:"hostIP" json:"hostIP,omitempty"`
HostPort string `mapstructure:"hostPort" json:"hostPort,omitempty"`
}
// GetKind implements Config.GetKind
func (c SimpleConfig) GetKind() string {
return "Simple"
}
func (c SimpleConfig) GetAPIVersion() string {
return ApiVersion
}
// ClusterConfig describes a single cluster config
type ClusterConfig struct {
config.TypeMeta `mapstructure:",squash"`
k3d.Cluster `mapstructure:",squash"`
ClusterCreateOpts k3d.ClusterCreateOpts `mapstructure:"options" json:"options"`
KubeconfigOpts SimpleConfigOptionsKubeconfig `mapstructure:"kubeconfig" json:"kubeconfig"`
}
// GetKind implements Config.GetKind
func (c ClusterConfig) GetKind() string {
return "Simple"
}
func (c ClusterConfig) GetAPIVersion() string {
return ApiVersion
}
// ClusterListConfig describes a list of clusters
type ClusterListConfig struct {
config.TypeMeta `mapstructure:",squash"`
Clusters []k3d.Cluster `mapstructure:"clusters" json:"clusters"`
}
func (c ClusterListConfig) GetKind() string {
return "Simple"
}
func (c ClusterListConfig) GetAPIVersion() string {
return ApiVersion
}
func GetConfigByKind(kind string) (config.Config, error) {
// determine config kind
switch strings.ToLower(kind) {
case "simple":
return SimpleConfig{}, nil
case "cluster":
return ClusterConfig{}, nil
case "clusterlist":
return ClusterListConfig{}, nil
case "":
return nil, fmt.Errorf("missing `kind` in config file")
default:
return nil, fmt.Errorf("unknown `kind` '%s' in config file", kind)
}
}

@ -27,7 +27,7 @@ import (
"time"
k3dc "github.com/k3d-io/k3d/v5/pkg/client"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
"github.com/k3d-io/k3d/v5/pkg/runtimes"
runtimeutil "github.com/k3d-io/k3d/v5/pkg/runtimes/util"
k3d "github.com/k3d-io/k3d/v5/pkg/types"

@ -26,7 +26,7 @@ import (
"context"
"testing"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha4"
conf "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
"github.com/k3d-io/k3d/v5/pkg/runtimes"
"github.com/spf13/viper"
)

@ -89,6 +89,11 @@ func TranslateNodeToContainer(node *k3d.Node) (*NodeInDocker, error) {
/* Labels */
containerConfig.Labels = node.RuntimeLabels // has to include the role
/* Ulimits */
if len(node.RuntimeUlimits) > 0 {
hostConfig.Ulimits = node.RuntimeUlimits
}
/* Auto-Restart */
if node.Restart {
hostConfig.RestartPolicy = docker.RestartPolicy{

@ -29,6 +29,7 @@ import (
"github.com/docker/go-connections/nat"
"inet.af/netaddr"
dockerunits "github.com/docker/go-units"
runtimeTypes "github.com/k3d-io/k3d/v5/pkg/runtimes/types"
"github.com/k3d-io/k3d/v5/pkg/types/k3s"
)
@ -284,28 +285,29 @@ type NodeIP struct {
// Node describes a k3d node
type Node struct {
Name string `json:"name,omitempty"`
Role Role `json:"role,omitempty"`
Image string `json:"image,omitempty"`
Volumes []string `json:"volumes,omitempty"`
Env []string `json:"env,omitempty"`
Cmd []string // filled automatically based on role
Args []string `json:"extraArgs,omitempty"`
Ports nat.PortMap `json:"portMappings,omitempty"`
Restart bool `json:"restart,omitempty"`
Created string `json:"created,omitempty"`
HostPidMode bool `json:"hostPidMode,omitempty"`
RuntimeLabels map[string]string `json:"runtimeLabels,omitempty"`
K3sNodeLabels map[string]string `json:"k3sNodeLabels,omitempty"`
Networks []string // filled automatically
ExtraHosts []string // filled automatically (docker specific?)
ServerOpts ServerOpts `json:"serverOpts,omitempty"`
AgentOpts AgentOpts `json:"agentOpts,omitempty"`
GPURequest string // filled automatically
Memory string // filled automatically
State NodeState // filled automatically
IP NodeIP // filled automatically -> refers solely to the cluster network
HookActions []NodeHook `json:"hooks,omitempty"`
Name string `json:"name,omitempty"`
Role Role `json:"role,omitempty"`
Image string `json:"image,omitempty"`
Volumes []string `json:"volumes,omitempty"`
Env []string `json:"env,omitempty"`
Cmd []string // filled automatically based on role
Args []string `json:"extraArgs,omitempty"`
Ports nat.PortMap `json:"portMappings,omitempty"`
Restart bool `json:"restart,omitempty"`
Created string `json:"created,omitempty"`
HostPidMode bool `json:"hostPidMode,omitempty"`
RuntimeLabels map[string]string `json:"runtimeLabels,omitempty"`
RuntimeUlimits []*dockerunits.Ulimit `json:"runtimeUlimits,omitempty"`
K3sNodeLabels map[string]string `json:"k3sNodeLabels,omitempty"`
Networks []string // filled automatically
ExtraHosts []string // filled automatically (docker specific?)
ServerOpts ServerOpts `json:"serverOpts,omitempty"`
AgentOpts AgentOpts `json:"agentOpts,omitempty"`
GPURequest string // filled automatically
Memory string // filled automatically
State NodeState // filled automatically
IP NodeIP // filled automatically -> refers solely to the cluster network
HookActions []NodeHook `json:"hooks,omitempty"`
}
// ServerOpts describes some additional server role specific opts

@ -1,4 +1,4 @@
apiVersion: k3d.io/v1alpha4
apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
name: testenvexpand

@ -1,4 +1,4 @@
apiVersion: k3d.io/v1alpha4
apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
name: test

@ -35,7 +35,7 @@ highlight "[START] ConfigTest $EXTRA_TITLE"
info "Creating cluster $clustername..."
cat <<EOF | $EXE cluster create --config=-
apiVersion: k3d.io/v1alpha4
apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
name: $clustername
@ -126,7 +126,7 @@ exec_in_node "k3d-$clustername-server-0" "cat /etc/rancher/k3s/registries.yaml"
info "Deleting cluster $clustername (using config file from stdin)..."
cat <<EOF | $EXE cluster delete --config=-
apiVersion: k3d.io/v1alpha4
apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
name: $clustername

@ -28,6 +28,7 @@ tempdir=$(mktemp -d)
$EXE config migrate "$CURR_DIR/assets/config_test_simple_migration_v1alpha2.yaml" "$tempdir/expected.yaml" || failed "failed on $CURR_DIR/assets/config_test_simple_migration_v1alpha2.yaml"
$EXE config migrate "$CURR_DIR/assets/config_test_simple_migration_v1alpha3.yaml" "$tempdir/actual.yaml" || failed "failed on $CURR_DIR/assets/config_test_simple_migration_v1alpha3.yaml"
# TODO: migrate to v1alpha4
# TODO: migrate to v1alpha5
diff "$tempdir/actual.yaml" "$tempdir/expected.yaml" || failed "config migration failed" && passed "config migration succeeded"

Loading…
Cancel
Save