diff --git a/.circleci/config.yml b/.circleci/config.yml index b5ecf9c..55abdd6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -49,7 +49,7 @@ jobs: sudo mv ./kind-linux-amd64 /usr/local/bin/kind - run: | set -ex - export TAG=v5.4.9 + export TAG=v5.6.0 curl -s https://raw.githubusercontent.com/rancher/k3d/main/install.sh | bash - run: | set -ex diff --git a/examples/k3d-config.yaml b/examples/k3d-config.yaml index 79b1776..0b8c072 100644 --- a/examples/k3d-config.yaml +++ b/examples/k3d-config.yaml @@ -4,5 +4,5 @@ kind: Cluster name: k3d-config product: k3d k3d: - v1alpha4Simple: + v1alpha5Simple: network: custom-network diff --git a/hack/make-rules/generated.sh b/hack/make-rules/generated.sh index 6aaaabb..aa365c0 100755 --- a/hack/make-rules/generated.sh +++ b/hack/make-rules/generated.sh @@ -1,6 +1,6 @@ #!/bin/bash -set -euo pipefail +set -exuo pipefail REPO_ROOT=$(dirname $(dirname $(dirname "$0"))) cd "${REPO_ROOT}" @@ -10,6 +10,10 @@ deepcopy-gen \ -i "./pkg/api/k3dv1alpha4" \ -O zz_generated.deepcopy \ --go-header-file hack/boilerplate.go.txt +deepcopy-gen \ + -i "./pkg/api/k3dv1alpha5" \ + -O zz_generated.deepcopy \ + --go-header-file hack/boilerplate.go.txt deepcopy-gen \ -i "./pkg/api" \ -O zz_generated.deepcopy \ diff --git a/pkg/api/k3dv1alpha5/doc.go b/pkg/api/k3dv1alpha5/doc.go new file mode 100644 index 0000000..03581ea --- /dev/null +++ b/pkg/api/k3dv1alpha5/doc.go @@ -0,0 +1,4 @@ +// Package k3dv1alpha5 implements the v1alpha4 apiVersion of k3d's config file. +// +// +k8s:deepcopy-gen=package +package k3dv1alpha5 diff --git a/pkg/api/k3dv1alpha5/types.go b/pkg/api/k3dv1alpha5/types.go new file mode 100644 index 0000000..84c5dd8 --- /dev/null +++ b/pkg/api/k3dv1alpha5/types.go @@ -0,0 +1,154 @@ +/* +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 k3dv1alpha5 + +import ( + "time" +) + +// TypeMeta partially copies apimachinery/pkg/apis/meta/v1.TypeMeta +// No need for a direct dependence; the fields are stable. +type TypeMeta struct { + Kind string `yaml:"kind,omitempty"` + APIVersion string `yaml:"apiVersion,omitempty"` +} + +type ObjectMeta struct { + Name string `mapstructure:"name,omitempty" yaml:"name,omitempty"` +} + +type VolumeWithNodeFilters struct { + Volume string `mapstructure:"volume" yaml:"volume,omitempty"` + NodeFilters []string `mapstructure:"nodeFilters" yaml:"nodeFilters,omitempty"` +} + +type PortWithNodeFilters struct { + Port string `mapstructure:"port" yaml:"port,omitempty"` + NodeFilters []string `mapstructure:"nodeFilters" yaml:"nodeFilters,omitempty"` +} + +type LabelWithNodeFilters struct { + Label string `mapstructure:"label" yaml:"label,omitempty"` + NodeFilters []string `mapstructure:"nodeFilters" yaml:"nodeFilters,omitempty"` +} + +type EnvVarWithNodeFilters struct { + EnvVar string `mapstructure:"envVar" yaml:"envVar,omitempty"` + NodeFilters []string `mapstructure:"nodeFilters" yaml:"nodeFilters,omitempty"` +} + +type K3sArgWithNodeFilters struct { + Arg string `mapstructure:"arg" yaml:"arg,omitempty"` + NodeFilters []string `mapstructure:"nodeFilters" yaml:"nodeFilters,omitempty"` +} + +type SimpleConfigRegistryCreateConfig struct { + Name string `mapstructure:"name" yaml:"name,omitempty"` + Host string `mapstructure:"host" yaml:"host,omitempty"` + HostPort string `mapstructure:"hostPort" yaml:"hostPort,omitempty"` + Image string `mapstructure:"image" yaml:"image,omitempty"` + Volumes []string `mapstructure:"volumes" yaml:"volumes,omitempty"` +} + +// SimpleConfigOptionsKubeconfig describes the set of options referring to the kubeconfig during cluster creation. +type SimpleConfigOptionsKubeconfig struct { + UpdateDefaultKubeconfig bool `mapstructure:"updateDefaultKubeconfig" yaml:"updateDefaultKubeconfig,omitempty"` // default: true + SwitchCurrentContext bool `mapstructure:"switchCurrentContext" yaml:"switchCurrentContext,omitempty"` //nolint:lll // default: true +} + +type SimpleConfigOptions struct { + K3dOptions SimpleConfigOptionsK3d `mapstructure:"k3d" yaml:"k3d"` + K3sOptions SimpleConfigOptionsK3s `mapstructure:"k3s" yaml:"k3s"` + KubeconfigOptions SimpleConfigOptionsKubeconfig `mapstructure:"kubeconfig" yaml:"kubeconfig"` + Runtime SimpleConfigOptionsRuntime `mapstructure:"runtime" yaml:"runtime"` +} + +type SimpleConfigOptionsRuntime struct { + GPURequest string `mapstructure:"gpuRequest" yaml:"gpuRequest,omitempty"` + ServersMemory string `mapstructure:"serversMemory" yaml:"serversMemory,omitempty"` + AgentsMemory string `mapstructure:"agentsMemory" yaml:"agentsMemory,omitempty"` + HostPidMode bool `mapstructure:"hostPidMode" yyaml:"hostPidMode,omitempty"` + Labels []LabelWithNodeFilters `mapstructure:"labels" yaml:"labels,omitempty"` + Ulimits []Ulimit `mapstructure:"ulimits" yaml:"ulimits,omitempty"` +} + +type Ulimit struct { + Name string `mapstructure:"name" yaml:"name"` + Soft int64 `mapstructure:"soft" yaml:"soft"` + Hard int64 `mapstructure:"hard" yaml:"hard"` +} + +type SimpleConfigOptionsK3d struct { + Wait bool `mapstructure:"wait" yaml:"wait"` + Timeout time.Duration `mapstructure:"timeout" yaml:"timeout,omitempty"` + DisableLoadbalancer bool `mapstructure:"disableLoadbalancer" yaml:"disableLoadbalancer"` + DisableImageVolume bool `mapstructure:"disableImageVolume" yaml:"disableImageVolume"` + NoRollback bool `mapstructure:"disableRollback" yaml:"disableRollback"` + Loadbalancer SimpleConfigOptionsK3dLoadbalancer `mapstructure:"loadbalancer" yaml:"loadbalancer,omitempty"` +} + +type SimpleConfigOptionsK3dLoadbalancer struct { + ConfigOverrides []string `mapstructure:"configOverrides" yaml:"configOverrides,omitempty"` +} + +type SimpleConfigOptionsK3s struct { + ExtraArgs []K3sArgWithNodeFilters `mapstructure:"extraArgs" yaml:"extraArgs,omitempty"` + NodeLabels []LabelWithNodeFilters `mapstructure:"nodeLabels" yaml:"nodeLabels,omitempty"` +} + +type SimpleConfigRegistries struct { + Use []string `mapstructure:"use" yaml:"use,omitempty"` + Create *SimpleConfigRegistryCreateConfig `mapstructure:"create" yaml:"create,omitempty"` + Config string `mapstructure:"config" yaml:"config,omitempty"` // registries.yaml (k3s config for containerd registry override) +} + +type SimpleConfigHostAlias struct { + IP string `mapstructure:"ip" yaml:"ip" json:"ip"` + Hostnames []string `mapstructure:"hostnames" yaml:"hostnames" json:"hostnames"` +} + +// SimpleConfig describes the toplevel k3d configuration file. +type SimpleConfig struct { + TypeMeta `mapstructure:",squash" yaml:",inline"` + ObjectMeta `mapstructure:"metadata" yaml:"metadata,omitempty"` + Servers int `mapstructure:"servers" yaml:"servers,omitempty"` //nolint:lll // default 1 + Agents int `mapstructure:"agents" yaml:"agents,omitempty"` //nolint:lll // default 0 + ExposeAPI SimpleExposureOpts `mapstructure:"kubeAPI" yaml:"kubeAPI,omitempty"` + Image string `mapstructure:"image" yaml:"image,omitempty"` + Network string `mapstructure:"network" yaml:"network,omitempty"` + Subnet string `mapstructure:"subnet" yaml:"subnet,omitempty"` + ClusterToken string `mapstructure:"token" yaml:"clusterToken,omitempty"` // default: auto-generated + Volumes []VolumeWithNodeFilters `mapstructure:"volumes" yaml:"volumes,omitempty"` + Ports []PortWithNodeFilters `mapstructure:"ports" yaml:"ports,omitempty"` + Options SimpleConfigOptions `mapstructure:"options" yaml:"options,omitempty"` + Env []EnvVarWithNodeFilters `mapstructure:"env" yaml:"env,omitempty"` + Registries SimpleConfigRegistries `mapstructure:"registries" yaml:"registries,omitempty"` + HostAliases []SimpleConfigHostAlias `mapstructure:"hostAliases" yaml:"hostAliases,omitempty"` +} + +// SimpleExposureOpts provides a simplified syntax compared to the original k3d.ExposureOpts +type SimpleExposureOpts struct { + Host string `mapstructure:"host" yaml:"host,omitempty"` + HostIP string `mapstructure:"hostIP" yaml:"hostIP,omitempty"` + HostPort string `mapstructure:"hostPort" yaml:"hostPort,omitempty"` +} diff --git a/pkg/api/k3dv1alpha5/zz_generated.deepcopy.go b/pkg/api/k3dv1alpha5/zz_generated.deepcopy.go new file mode 100644 index 0000000..d63b80e --- /dev/null +++ b/pkg/api/k3dv1alpha5/zz_generated.deepcopy.go @@ -0,0 +1,440 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2020 Tilt Dev + +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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package k3dv1alpha5 + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvVarWithNodeFilters) DeepCopyInto(out *EnvVarWithNodeFilters) { + *out = *in + if in.NodeFilters != nil { + in, out := &in.NodeFilters, &out.NodeFilters + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvVarWithNodeFilters. +func (in *EnvVarWithNodeFilters) DeepCopy() *EnvVarWithNodeFilters { + if in == nil { + return nil + } + out := new(EnvVarWithNodeFilters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *K3sArgWithNodeFilters) DeepCopyInto(out *K3sArgWithNodeFilters) { + *out = *in + if in.NodeFilters != nil { + in, out := &in.NodeFilters, &out.NodeFilters + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K3sArgWithNodeFilters. +func (in *K3sArgWithNodeFilters) DeepCopy() *K3sArgWithNodeFilters { + if in == nil { + return nil + } + out := new(K3sArgWithNodeFilters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LabelWithNodeFilters) DeepCopyInto(out *LabelWithNodeFilters) { + *out = *in + if in.NodeFilters != nil { + in, out := &in.NodeFilters, &out.NodeFilters + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LabelWithNodeFilters. +func (in *LabelWithNodeFilters) DeepCopy() *LabelWithNodeFilters { + if in == nil { + return nil + } + out := new(LabelWithNodeFilters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectMeta) DeepCopyInto(out *ObjectMeta) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectMeta. +func (in *ObjectMeta) DeepCopy() *ObjectMeta { + if in == nil { + return nil + } + out := new(ObjectMeta) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PortWithNodeFilters) DeepCopyInto(out *PortWithNodeFilters) { + *out = *in + if in.NodeFilters != nil { + in, out := &in.NodeFilters, &out.NodeFilters + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PortWithNodeFilters. +func (in *PortWithNodeFilters) DeepCopy() *PortWithNodeFilters { + if in == nil { + return nil + } + out := new(PortWithNodeFilters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SimpleConfig) DeepCopyInto(out *SimpleConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ObjectMeta = in.ObjectMeta + out.ExposeAPI = in.ExposeAPI + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]VolumeWithNodeFilters, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]PortWithNodeFilters, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Options.DeepCopyInto(&out.Options) + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]EnvVarWithNodeFilters, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Registries.DeepCopyInto(&out.Registries) + if in.HostAliases != nil { + in, out := &in.HostAliases, &out.HostAliases + *out = make([]SimpleConfigHostAlias, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SimpleConfig. +func (in *SimpleConfig) DeepCopy() *SimpleConfig { + if in == nil { + return nil + } + out := new(SimpleConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SimpleConfigHostAlias) DeepCopyInto(out *SimpleConfigHostAlias) { + *out = *in + if in.Hostnames != nil { + in, out := &in.Hostnames, &out.Hostnames + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SimpleConfigHostAlias. +func (in *SimpleConfigHostAlias) DeepCopy() *SimpleConfigHostAlias { + if in == nil { + return nil + } + out := new(SimpleConfigHostAlias) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SimpleConfigOptions) DeepCopyInto(out *SimpleConfigOptions) { + *out = *in + in.K3dOptions.DeepCopyInto(&out.K3dOptions) + in.K3sOptions.DeepCopyInto(&out.K3sOptions) + out.KubeconfigOptions = in.KubeconfigOptions + in.Runtime.DeepCopyInto(&out.Runtime) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SimpleConfigOptions. +func (in *SimpleConfigOptions) DeepCopy() *SimpleConfigOptions { + if in == nil { + return nil + } + out := new(SimpleConfigOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SimpleConfigOptionsK3d) DeepCopyInto(out *SimpleConfigOptionsK3d) { + *out = *in + in.Loadbalancer.DeepCopyInto(&out.Loadbalancer) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SimpleConfigOptionsK3d. +func (in *SimpleConfigOptionsK3d) DeepCopy() *SimpleConfigOptionsK3d { + if in == nil { + return nil + } + out := new(SimpleConfigOptionsK3d) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SimpleConfigOptionsK3dLoadbalancer) DeepCopyInto(out *SimpleConfigOptionsK3dLoadbalancer) { + *out = *in + if in.ConfigOverrides != nil { + in, out := &in.ConfigOverrides, &out.ConfigOverrides + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SimpleConfigOptionsK3dLoadbalancer. +func (in *SimpleConfigOptionsK3dLoadbalancer) DeepCopy() *SimpleConfigOptionsK3dLoadbalancer { + if in == nil { + return nil + } + out := new(SimpleConfigOptionsK3dLoadbalancer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SimpleConfigOptionsK3s) DeepCopyInto(out *SimpleConfigOptionsK3s) { + *out = *in + if in.ExtraArgs != nil { + in, out := &in.ExtraArgs, &out.ExtraArgs + *out = make([]K3sArgWithNodeFilters, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.NodeLabels != nil { + in, out := &in.NodeLabels, &out.NodeLabels + *out = make([]LabelWithNodeFilters, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SimpleConfigOptionsK3s. +func (in *SimpleConfigOptionsK3s) DeepCopy() *SimpleConfigOptionsK3s { + if in == nil { + return nil + } + out := new(SimpleConfigOptionsK3s) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SimpleConfigOptionsKubeconfig) DeepCopyInto(out *SimpleConfigOptionsKubeconfig) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SimpleConfigOptionsKubeconfig. +func (in *SimpleConfigOptionsKubeconfig) DeepCopy() *SimpleConfigOptionsKubeconfig { + if in == nil { + return nil + } + out := new(SimpleConfigOptionsKubeconfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SimpleConfigOptionsRuntime) DeepCopyInto(out *SimpleConfigOptionsRuntime) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make([]LabelWithNodeFilters, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Ulimits != nil { + in, out := &in.Ulimits, &out.Ulimits + *out = make([]Ulimit, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SimpleConfigOptionsRuntime. +func (in *SimpleConfigOptionsRuntime) DeepCopy() *SimpleConfigOptionsRuntime { + if in == nil { + return nil + } + out := new(SimpleConfigOptionsRuntime) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SimpleConfigRegistries) DeepCopyInto(out *SimpleConfigRegistries) { + *out = *in + if in.Use != nil { + in, out := &in.Use, &out.Use + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Create != nil { + in, out := &in.Create, &out.Create + *out = new(SimpleConfigRegistryCreateConfig) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SimpleConfigRegistries. +func (in *SimpleConfigRegistries) DeepCopy() *SimpleConfigRegistries { + if in == nil { + return nil + } + out := new(SimpleConfigRegistries) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SimpleConfigRegistryCreateConfig) DeepCopyInto(out *SimpleConfigRegistryCreateConfig) { + *out = *in + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SimpleConfigRegistryCreateConfig. +func (in *SimpleConfigRegistryCreateConfig) DeepCopy() *SimpleConfigRegistryCreateConfig { + if in == nil { + return nil + } + out := new(SimpleConfigRegistryCreateConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SimpleExposureOpts) DeepCopyInto(out *SimpleExposureOpts) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SimpleExposureOpts. +func (in *SimpleExposureOpts) DeepCopy() *SimpleExposureOpts { + if in == nil { + return nil + } + out := new(SimpleExposureOpts) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TypeMeta) DeepCopyInto(out *TypeMeta) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TypeMeta. +func (in *TypeMeta) DeepCopy() *TypeMeta { + if in == nil { + return nil + } + out := new(TypeMeta) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Ulimit) DeepCopyInto(out *Ulimit) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Ulimit. +func (in *Ulimit) DeepCopy() *Ulimit { + if in == nil { + return nil + } + out := new(Ulimit) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VolumeWithNodeFilters) DeepCopyInto(out *VolumeWithNodeFilters) { + *out = *in + if in.NodeFilters != nil { + in, out := &in.NodeFilters, &out.NodeFilters + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeWithNodeFilters. +func (in *VolumeWithNodeFilters) DeepCopy() *VolumeWithNodeFilters { + if in == nil { + return nil + } + out := new(VolumeWithNodeFilters) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/api/types.go b/pkg/api/types.go index a315d39..cf987ca 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -6,6 +6,7 @@ import ( "sigs.k8s.io/kind/pkg/apis/config/v1alpha4" "github.com/tilt-dev/ctlptl/pkg/api/k3dv1alpha4" + "github.com/tilt-dev/ctlptl/pkg/api/k3dv1alpha5" ) // TypeMeta partially copies apimachinery/pkg/apis/meta/v1.TypeMeta @@ -134,7 +135,14 @@ type MinikubeCluster struct { // ctlptl's logic for diffing clusters and applying changes is less robust // for cluster-specific configs. type K3DCluster struct { - // K3D's own cluster config format. + // K3D's own cluster config format, v1alpha5. + // + // Documentation: https://k3d.io/v5.6.0/usage/configfile/ + // + // Uses this schema: https://github.com/k3d-io/k3d/blob/v5.6.0/pkg/config/v1alpha5/types.go + V1Alpha5Simple *k3dv1alpha5.SimpleConfig `json:"v1alpha5Simple,omitempty" yaml:"v1alpha5Simple,omitempty"` + + // K3D's own cluster config format, v1alpha4. // // Documentation: https://k3d.io/v5.4.6/usage/configfile/ // diff --git a/pkg/api/zz_generated.deepcopy.go b/pkg/api/zz_generated.deepcopy.go index e9ca479..e76bc95 100644 --- a/pkg/api/zz_generated.deepcopy.go +++ b/pkg/api/zz_generated.deepcopy.go @@ -23,6 +23,7 @@ package api import ( k3dv1alpha4 "github.com/tilt-dev/ctlptl/pkg/api/k3dv1alpha4" + k3dv1alpha5 "github.com/tilt-dev/ctlptl/pkg/api/k3dv1alpha5" localregistrygo "github.com/tilt-dev/localregistry-go" runtime "k8s.io/apimachinery/pkg/runtime" v1alpha4 "sigs.k8s.io/kind/pkg/apis/config/v1alpha4" @@ -126,6 +127,11 @@ func (in *ClusterStatus) DeepCopy() *ClusterStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *K3DCluster) DeepCopyInto(out *K3DCluster) { *out = *in + if in.V1Alpha5Simple != nil { + in, out := &in.V1Alpha5Simple, &out.V1Alpha5Simple + *out = new(k3dv1alpha5.SimpleConfig) + (*in).DeepCopyInto(*out) + } if in.V1Alpha4Simple != nil { in, out := &in.V1Alpha4Simple, &out.V1Alpha4Simple *out = new(k3dv1alpha4.SimpleConfig) diff --git a/pkg/cluster/admin_k3d.go b/pkg/cluster/admin_k3d.go index 29dbbd8..631abed 100644 --- a/pkg/cluster/admin_k3d.go +++ b/pkg/cluster/admin_k3d.go @@ -17,11 +17,15 @@ import ( cexec "github.com/tilt-dev/ctlptl/internal/exec" "github.com/tilt-dev/ctlptl/pkg/api" "github.com/tilt-dev/ctlptl/pkg/api/k3dv1alpha4" + "github.com/tilt-dev/ctlptl/pkg/api/k3dv1alpha5" ) -// Support for v1alpha4 format. +// Support for v1alpha4 file format starts in 5.3.0. var v5_3 = semver.MustParse("5.3.0") +// Support for v1alpha5 file format starts in 5.5.0. +var v5_5 = semver.MustParse("5.5.0") + // k3dAdmin uses the k3d CLI to manipulate a k3d cluster, // once the underlying machine has been setup. type k3dAdmin struct { @@ -55,13 +59,21 @@ func (a *k3dAdmin) Create(ctx context.Context, desired *api.Cluster, registry *a return errors.Wrap(err, "detecting k3d version") } - if desired.K3D != nil && desired.K3D.V1Alpha4Simple != nil && k3dV.LT(v5_3) { - return fmt.Errorf("k3d v1alpha4 config file only supported on v5.3+") + if desired.K3D != nil { + if desired.K3D.V1Alpha4Simple != nil && k3dV.LT(v5_3) { + return fmt.Errorf("k3d v1alpha4 config file only supported on v5.3+") + } + if desired.K3D.V1Alpha4Simple != nil && k3dV.LT(v5_5) { + return fmt.Errorf("k3d v1alpha5 config file only supported on v5.5+") + } + if desired.K3D.V1Alpha5Simple != nil && desired.K3D.V1Alpha4Simple != nil { + return fmt.Errorf("k3d config invalid: only one format allowed, both specified") + } } // We generate a cluster config on all versions // because it does some useful validation. - k3dConfig, err := a.clusterConfig(desired, registry) + k3dConfig, err := a.clusterConfig(desired, registry, k3dV) if err != nil { return errors.Wrap(err, "creating k3d cluster") } @@ -72,7 +84,7 @@ func (a *k3dAdmin) Create(ctx context.Context, desired *api.Cluster, registry *a if k3dV.LT(v5_3) { // 5.2 and below - args := []string{"cluster", "create", k3dConfig.Name} + args := []string{"cluster", "create", k3dConfig.name()} if registry != nil { args = append(args, "--registry-use", registry.Name) } @@ -90,12 +102,12 @@ func (a *k3dAdmin) Create(ctx context.Context, desired *api.Cluster, registry *a // 5.3 and above. buf := bytes.NewBuffer(nil) encoder := yaml.NewEncoder(buf) - err = encoder.Encode(k3dConfig) + err = encoder.Encode(k3dConfig.forEncoding()) if err != nil { return errors.Wrap(err, "creating k3d cluster") } - args := []string{"cluster", "create", k3dConfig.Name, "--config", "-"} + args := []string{"cluster", "create", k3dConfig.name(), "--config", "-"} err = a.runner.RunIO(ctx, genericclioptions.IOStreams{In: buf, Out: a.iostreams.Out, ErrOut: a.iostreams.ErrOut}, "k3d", args...) @@ -144,24 +156,71 @@ func (a *k3dAdmin) version(ctx context.Context) (semver.Version, error) { return result, nil } -func (a *k3dAdmin) clusterConfig(desired *api.Cluster, registry *api.Registry) (*k3dv1alpha4.SimpleConfig, error) { - var k3dConfig *k3dv1alpha4.SimpleConfig - if desired.K3D == nil || desired.K3D.V1Alpha4Simple == nil { - k3dConfig = &k3dv1alpha4.SimpleConfig{} +func (a *k3dAdmin) clusterConfig(desired *api.Cluster, registry *api.Registry, k3dv semver.Version) (*k3dClusterConfig, error) { + var v4 *k3dv1alpha4.SimpleConfig + var v5 *k3dv1alpha5.SimpleConfig + if desired.K3D != nil && desired.K3D.V1Alpha5Simple != nil { + v5 = desired.K3D.V1Alpha5Simple.DeepCopy() + } else if desired.K3D != nil && desired.K3D.V1Alpha4Simple != nil { + v4 = desired.K3D.V1Alpha4Simple.DeepCopy() + } else if !k3dv.LT(v5_5) { + v5 = &k3dv1alpha5.SimpleConfig{} + } else { + v4 = &k3dv1alpha4.SimpleConfig{} + } + + if v5 != nil { + v5.Kind = "Simple" + v5.APIVersion = "k3d.io/v1alpha5" } else { - k3dConfig = desired.K3D.V1Alpha4Simple.DeepCopy() + v4.Kind = "Simple" + v4.APIVersion = "k3d.io/v1alpha4" } - k3dConfig.Kind = "Simple" - k3dConfig.APIVersion = "k3d.io/v1alpha4" clusterName := desired.Name if !strings.HasPrefix(clusterName, "k3d-") { return nil, fmt.Errorf("all k3d clusters must have a name with the prefix k3d-*") } - k3dConfig.Name = strings.TrimPrefix(clusterName, "k3d-") - if registry != nil { - k3dConfig.Registries.Use = append(k3dConfig.Registries.Use, registry.Name) + if v5 != nil { + v5.Name = strings.TrimPrefix(clusterName, "k3d-") + if registry != nil { + v5.Registries.Use = append(v5.Registries.Use, registry.Name) + } + } else { + v4.Name = strings.TrimPrefix(clusterName, "k3d-") + if registry != nil { + v4.Registries.Use = append(v4.Registries.Use, registry.Name) + } + } + return &k3dClusterConfig{ + v1Alpha5: v5, + v1Alpha4: v4, + }, nil +} + +// Helper struct for serializing different file formats. +type k3dClusterConfig struct { + v1Alpha5 *k3dv1alpha5.SimpleConfig + v1Alpha4 *k3dv1alpha4.SimpleConfig +} + +func (c *k3dClusterConfig) forEncoding() interface{} { + if c.v1Alpha5 != nil { + return c.v1Alpha5 + } + if c.v1Alpha4 != nil { + return c.v1Alpha4 + } + return nil +} + +func (c *k3dClusterConfig) name() string { + if c.v1Alpha5 != nil { + return c.v1Alpha5.Name + } + if c.v1Alpha4 != nil { + return c.v1Alpha4.Name } - return k3dConfig, nil + return "" } diff --git a/pkg/cluster/admin_k3d_test.go b/pkg/cluster/admin_k3d_test.go index 6a922f0..b3b5ff4 100644 --- a/pkg/cluster/admin_k3d_test.go +++ b/pkg/cluster/admin_k3d_test.go @@ -13,6 +13,7 @@ import ( "github.com/tilt-dev/ctlptl/internal/exec" "github.com/tilt-dev/ctlptl/pkg/api" "github.com/tilt-dev/ctlptl/pkg/api/k3dv1alpha4" + "github.com/tilt-dev/ctlptl/pkg/api/k3dv1alpha5" ) func TestK3DStartFlagsV4(t *testing.T) { @@ -40,7 +41,7 @@ func TestK3DStartFlagsV5(t *testing.T) { ctx := context.Background() v, err := f.a.version(ctx) require.NoError(t, err) - assert.Equal(t, "5.4.6", v.String()) + assert.Equal(t, "5.6.0", v.String()) err = f.a.Create(ctx, &api.Cluster{ Name: "k3d-my-cluster", @@ -66,6 +67,61 @@ registries: `) } +func TestK3DV1alpha5File(t *testing.T) { + f := newK3DFixture() + + ctx := context.Background() + v, err := f.a.version(ctx) + require.NoError(t, err) + assert.Equal(t, "5.6.0", v.String()) + + err = f.a.Create(ctx, &api.Cluster{ + Name: "k3d-my-cluster", + K3D: &api.K3DCluster{ + V1Alpha5Simple: &k3dv1alpha5.SimpleConfig{ + Network: "bar", + }, + }, + }, &api.Registry{Name: "my-reg"}) + require.NoError(t, err) + assert.Equal(t, []string{ + "k3d", "cluster", "create", "my-cluster", + "--config", "-", + }, f.runner.LastArgs) + assert.Equal(t, f.runner.LastStdin, `kind: Simple +apiVersion: k3d.io/v1alpha5 +metadata: + name: my-cluster +network: bar +registries: + use: + - my-reg +`) +} + +func TestK3DV1alpha4FileOnOldVersions(t *testing.T) { + f := newK3DFixture() + f.version = "v5.4.0" + + ctx := context.Background() + err := f.a.Create(ctx, &api.Cluster{ + Name: "k3d-my-cluster", + }, &api.Registry{Name: "my-reg"}) + require.NoError(t, err) + assert.Equal(t, []string{ + "k3d", "cluster", "create", "my-cluster", + "--config", "-", + }, f.runner.LastArgs) + assert.Equal(t, f.runner.LastStdin, `kind: Simple +apiVersion: k3d.io/v1alpha4 +metadata: + name: my-cluster +registries: + use: + - my-reg +`) +} + type k3dFixture struct { runner *exec.FakeCmdRunner a *k3dAdmin @@ -75,7 +131,7 @@ type k3dFixture struct { func newK3DFixture() *k3dFixture { iostreams := genericclioptions.IOStreams{Out: os.Stdout, ErrOut: os.Stderr} f := &k3dFixture{ - version: "v5.4.6", + version: "v5.6.0", } f.runner = exec.NewFakeCmdRunner(func(argv []string) string { if argv[1] == "version" {