mirror of https://github.com/k3d-io/k3d
[NEW VERSION v4] Merge pull request #447 from rancher/main-v4
commit
5092a90d56
@ -0,0 +1,69 @@ |
||||
# Changelog |
||||
|
||||
## v4.0.0 |
||||
|
||||
### Breaking Changes |
||||
|
||||
#### Module |
||||
|
||||
**If you're using k3d as a Go module, please have a look into the code to see all the changes!** |
||||
|
||||
- We're open for chats via Slack or GitHub discussions |
||||
|
||||
- Module is now on `github.com/rancher/k3d/v4` due to lots of breaking changes |
||||
- `pkg/cluster` is now `pkg/client` |
||||
- `ClusterCreate` and `NodeCreate` don't start the entities (containers) anymore |
||||
- `ClusterRun` and `NodeRun` orchestrate the new Create and Start functionality |
||||
- New config flow: CLIConfig (SimpleConfig) -> ClusterConfig -> Cluster + Opts |
||||
|
||||
#### CLI |
||||
|
||||
- Some flags changed to also use `noun-action` syntax |
||||
- e.g. `--switch-context --update-default-kubeconfig` -> `--kubeconfig-switch-context --kubeconfig-update-default` |
||||
- this eases grouping and visibility |
||||
|
||||
### Changes |
||||
|
||||
#### Features |
||||
|
||||
- Registry Support |
||||
- k3d-managed registry like we had it in k3d v1.x |
||||
- Option 1: default settings, paired with cluster creation |
||||
- `k3d cluster create --registry-create` -> New registry for that cluster |
||||
- `k3d cluster create --registry-use` -> Re-use existing registry |
||||
- Option 2: customized, managed stand-alone |
||||
- `k3d registry [create/start/stop/delete]` |
||||
- Check the documentation, help text and tutorials for more details |
||||
- Communicate managed registry using the LocalRegistryHostingV1 spec from [KEP-1755](https://github.com/kubernetes/enhancements/blob/0d69f7cea6fbe73a7d70fab569c6898f5ccb7be0/keps/sig-cluster-lifecycle/generic/1755-communicating-a-local-registry/README.md) |
||||
- interesting especially for tools that reload images, like Tilt or Skaffold |
||||
|
||||
- Config File Support |
||||
- Put all your CLI-Arguments/Flags into a more readable config file and re-use it everywhere (keep it in your repo) |
||||
- Note: this is not always a 1:1 matching in naming/syntax/semantics |
||||
- `k3d cluster create --config myconfig.yaml` |
||||
|
||||
```yaml |
||||
apiVersion: k3d.io/v1alpha1 |
||||
kind: Simple |
||||
name: mycluster |
||||
servers: 3 |
||||
agents: 2 |
||||
ports: |
||||
- port: 8080:80 |
||||
nodeFilters: |
||||
- loadbalancer |
||||
``` |
||||
|
||||
- Check out our test cases in [pkg/config/test_assets/](./pkg/config/test_assets/) for more config file examples |
||||
|
||||
- [WIP] Support for Lifecycle Hooks |
||||
- Run any executable at specific stages during the cluster and node lifecycles |
||||
- e.g. we modify the `registries.yaml` in the `preStart` stage of nodes |
||||
- Guides will follow |
||||
|
||||
#### Misc |
||||
|
||||
- Now building with Go 1.15 |
||||
- same for the k3d-tools code |
||||
- updated dependencies (including Docker v20.10) |
||||
- tests/e2e: add E2E_INCLUDE and rename E2E_SKIP to E2E_EXCLUDE |
@ -0,0 +1,46 @@ |
||||
/* |
||||
Copyright © 2020 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 config |
||||
|
||||
import ( |
||||
log "github.com/sirupsen/logrus" |
||||
"github.com/spf13/cobra" |
||||
) |
||||
|
||||
// NewCmdConfig returns a new cobra command
|
||||
func NewCmdConfig() *cobra.Command { |
||||
cmd := &cobra.Command{ |
||||
Use: "config", |
||||
Short: "Work with config file(s)", |
||||
Long: `Work with config file(s)`, |
||||
Run: func(cmd *cobra.Command, args []string) { |
||||
if err := cmd.Help(); err != nil { |
||||
log.Errorln("Couldn't get help text") |
||||
log.Fatalln(err) |
||||
} |
||||
}, |
||||
} |
||||
|
||||
cmd.AddCommand(NewCmdConfigInit()) |
||||
|
||||
return cmd |
||||
} |
@ -0,0 +1,74 @@ |
||||
/* |
||||
Copyright © 2020 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 config |
||||
|
||||
import ( |
||||
"fmt" |
||||
"os" |
||||
|
||||
config "github.com/rancher/k3d/v4/pkg/config/v1alpha1" |
||||
log "github.com/sirupsen/logrus" |
||||
"github.com/spf13/cobra" |
||||
) |
||||
|
||||
// NewCmdConfigInit returns a new cobra command
|
||||
func NewCmdConfigInit() *cobra.Command { |
||||
var output string |
||||
var force bool |
||||
|
||||
cmd := &cobra.Command{ |
||||
Use: "init", |
||||
Aliases: []string{"create"}, |
||||
Run: func(cmd *cobra.Command, args []string) { |
||||
log.Infoln("COMING SOON: print a basic k3d config with default pre-filled.") |
||||
if output == "-" { |
||||
fmt.Println(config.DefaultConfig) |
||||
} else { |
||||
// check if file exists
|
||||
var file *os.File |
||||
var err error |
||||
_, err = os.Stat(output) |
||||
if os.IsNotExist(err) || force { |
||||
// create/overwrite file
|
||||
file, err = os.Create(output) |
||||
if err != nil { |
||||
log.Fatalf("Failed to create/overwrite output file: %s", err) |
||||
} |
||||
// write content
|
||||
if _, err = file.WriteString(config.DefaultConfig); err != nil { |
||||
log.Fatalf("Failed to write to output file: %+v", err) |
||||
} |
||||
} else if err != nil { |
||||
log.Fatalf("Failed to stat output file: %+v", err) |
||||
} else { |
||||
log.Errorln("Output file exists and --force was not set") |
||||
os.Exit(1) |
||||
} |
||||
} |
||||
}, |
||||
} |
||||
|
||||
cmd.Flags().StringVarP(&output, "output", "o", "k3d-default.yaml", "Write a default k3d config") |
||||
cmd.Flags().BoolVarP(&force, "force", "f", false, "Force overwrite of target file") |
||||
|
||||
return cmd |
||||
} |
@ -0,0 +1,44 @@ |
||||
/* |
||||
Copyright © 2020 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 config |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
log "github.com/sirupsen/logrus" |
||||
"github.com/spf13/cobra" |
||||
"github.com/spf13/viper" |
||||
) |
||||
|
||||
// NewCmdConfig returns a new cobra command
|
||||
func NewCmdConfigView() *cobra.Command { |
||||
cmd := &cobra.Command{ |
||||
Use: "view", |
||||
Aliases: []string{"show"}, |
||||
Run: func(cmd *cobra.Command, args []string) { |
||||
log.Debugln("print config") |
||||
fmt.Printf("%+v", viper.AllSettings()) |
||||
log.Debugln("printed config") |
||||
}, |
||||
} |
||||
return cmd |
||||
} |
@ -0,0 +1,58 @@ |
||||
/* |
||||
Copyright © 2020 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 registry |
||||
|
||||
import ( |
||||
log "github.com/sirupsen/logrus" |
||||
"github.com/spf13/cobra" |
||||
) |
||||
|
||||
// NewCmdRegistry returns a new cobra command
|
||||
func NewCmdRegistry() *cobra.Command { |
||||
|
||||
// create new cobra command
|
||||
cmd := &cobra.Command{ |
||||
Use: "registry", |
||||
Aliases: []string{"registries", "reg"}, |
||||
Short: "Manage registry/registries", |
||||
Long: `Manage registry/registries`, |
||||
Run: func(cmd *cobra.Command, args []string) { |
||||
if err := cmd.Help(); err != nil { |
||||
log.Errorln("Couldn't get help text") |
||||
log.Fatalln(err) |
||||
} |
||||
}, |
||||
} |
||||
|
||||
// add subcommands
|
||||
cmd.AddCommand(NewCmdRegistryCreate()) |
||||
cmd.AddCommand(NewCmdRegistryStart()) |
||||
cmd.AddCommand(NewCmdRegistryStop()) |
||||
cmd.AddCommand(NewCmdRegistryDelete()) |
||||
cmd.AddCommand(NewCmdRegistryList()) |
||||
// cmd.AddCommand(NewCmdRegistryConnect()) // TODO: registry connect requires reload capabilities for containerd config
|
||||
|
||||
// add flags
|
||||
|
||||
// done
|
||||
return cmd |
||||
} |
@ -0,0 +1,29 @@ |
||||
/* |
||||
Copyright © 2020 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 registry |
||||
|
||||
import "github.com/spf13/cobra" |
||||
|
||||
// NewCmdRegistryConnect creates a new cobra command
|
||||
func NewCmdRegistryConnect() *cobra.Command { |
||||
return &cobra.Command{} |
||||
} |
@ -0,0 +1,117 @@ |
||||
/* |
||||
Copyright © 2020 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 registry |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
log "github.com/sirupsen/logrus" |
||||
|
||||
"github.com/rancher/k3d/v4/pkg/runtimes" |
||||
k3d "github.com/rancher/k3d/v4/pkg/types" |
||||
|
||||
"github.com/rancher/k3d/v4/pkg/client" |
||||
|
||||
cliutil "github.com/rancher/k3d/v4/cmd/util" |
||||
"github.com/spf13/cobra" |
||||
) |
||||
|
||||
type regCreatePreProcessedFlags struct { |
||||
Port string |
||||
Clusters []string |
||||
} |
||||
|
||||
type regCreateFlags struct { |
||||
Image string |
||||
} |
||||
|
||||
// NewCmdRegistryCreate returns a new cobra command
|
||||
func NewCmdRegistryCreate() *cobra.Command { |
||||
|
||||
flags := ®CreateFlags{} |
||||
ppFlags := ®CreatePreProcessedFlags{} |
||||
|
||||
// create new command
|
||||
cmd := &cobra.Command{ |
||||
Use: "create NAME", |
||||
Short: "Create a new registry", |
||||
Long: `Create a new registry.`, |
||||
Args: cobra.MaximumNArgs(1), // maximum one name accepted
|
||||
Run: func(cmd *cobra.Command, args []string) { |
||||
reg, clusters := parseCreateRegistryCmd(cmd, args, flags, ppFlags) |
||||
regNode, err := client.RegistryRun(cmd.Context(), runtimes.SelectedRuntime, reg) |
||||
if err != nil { |
||||
log.Fatalln(err) |
||||
} |
||||
if err := client.RegistryConnectClusters(cmd.Context(), runtimes.SelectedRuntime, regNode, clusters); err != nil { |
||||
log.Errorln(err) |
||||
} |
||||
}, |
||||
} |
||||
|
||||
// add flags
|
||||
|
||||
// TODO: connecting to clusters requires non-existing config reload functionality in containerd
|
||||
cmd.Flags().StringArrayVarP(&ppFlags.Clusters, "cluster", "c", nil, "[NotReady] Select the cluster(s) that the registry shall connect to.") |
||||
if err := cmd.RegisterFlagCompletionFunc("cluster", cliutil.ValidArgsAvailableClusters); err != nil { |
||||
log.Fatalln("Failed to register flag completion for '--cluster'", err) |
||||
} |
||||
if err := cmd.Flags().MarkHidden("cluster"); err != nil { |
||||
log.Fatalln("Failed to hide --cluster flag on registry create command") |
||||
} |
||||
|
||||
cmd.Flags().StringVarP(&flags.Image, "image", "i", fmt.Sprintf("%s:%s", k3d.DefaultRegistryImageRepo, k3d.DefaultRegistryImageTag), "Specify image used for the registry") |
||||
|
||||
cmd.Flags().StringVarP(&ppFlags.Port, "port", "p", "random", "Select which port the registry should be listening on on your machine (localhost) (Format: `[HOST:]HOSTPORT`)\n - Example: `k3d registry create --port 0.0.0.0:5111`") |
||||
|
||||
// done
|
||||
return cmd |
||||
} |
||||
|
||||
// parseCreateRegistryCmd parses the command input into variables required to create a registry
|
||||
func parseCreateRegistryCmd(cmd *cobra.Command, args []string, flags *regCreateFlags, ppFlags *regCreatePreProcessedFlags) (*k3d.Registry, []*k3d.Cluster) { |
||||
|
||||
// --cluster
|
||||
clusters := []*k3d.Cluster{} |
||||
for _, name := range ppFlags.Clusters { |
||||
clusters = append(clusters, |
||||
&k3d.Cluster{ |
||||
Name: name, |
||||
}, |
||||
) |
||||
} |
||||
|
||||
// --port
|
||||
exposePort, err := cliutil.ParsePortExposureSpec(ppFlags.Port, k3d.DefaultRegistryPort) |
||||
if err != nil { |
||||
log.Errorln("Failed to parse registry port") |
||||
log.Fatalln(err) |
||||
} |
||||
|
||||
// set the name for the registry node
|
||||
registryName := "" |
||||
if len(args) > 0 { |
||||
registryName = fmt.Sprintf("%s-%s", k3d.DefaultObjectNamePrefix, args[0]) |
||||
} |
||||
|
||||
return &k3d.Registry{Host: registryName, Image: flags.Image, ExposureOpts: *exposePort}, clusters |
||||
} |
@ -0,0 +1,97 @@ |
||||
/* |
||||
Copyright © 2020 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 registry |
||||
|
||||
import ( |
||||
"github.com/rancher/k3d/v4/cmd/util" |
||||
"github.com/rancher/k3d/v4/pkg/client" |
||||
"github.com/rancher/k3d/v4/pkg/runtimes" |
||||
k3d "github.com/rancher/k3d/v4/pkg/types" |
||||
log "github.com/sirupsen/logrus" |
||||
"github.com/spf13/cobra" |
||||
) |
||||
|
||||
// NewCmdRegistryDelete returns a new cobra command
|
||||
func NewCmdRegistryDelete() *cobra.Command { |
||||
|
||||
// create new cobra command
|
||||
cmd := &cobra.Command{ |
||||
Use: "delete (NAME | --all)", |
||||
Short: "Delete registry/registries.", |
||||
Long: `Delete registry/registries.`, |
||||
Args: cobra.MinimumNArgs(1), // at least one node has to be specified
|
||||
ValidArgsFunction: util.ValidArgsAvailableRegistries, |
||||
Run: func(cmd *cobra.Command, args []string) { |
||||
|
||||
nodes := parseRegistryDeleteCmd(cmd, args) |
||||
|
||||
if len(nodes) == 0 { |
||||
log.Infoln("No nodes found") |
||||
} else { |
||||
for _, node := range nodes { |
||||
if err := client.NodeDelete(cmd.Context(), runtimes.SelectedRuntime, node); err != nil { |
||||
log.Fatalln(err) |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
} |
||||
|
||||
// add subcommands
|
||||
|
||||
// add flags
|
||||
cmd.Flags().BoolP("all", "a", false, "Delete all existing registries") |
||||
|
||||
// done
|
||||
return cmd |
||||
} |
||||
|
||||
// parseRegistryDeleteCmd parses the command input into variables required to delete nodes
|
||||
func parseRegistryDeleteCmd(cmd *cobra.Command, args []string) []*k3d.Node { |
||||
|
||||
// --all
|
||||
var nodes []*k3d.Node |
||||
|
||||
if all, err := cmd.Flags().GetBool("all"); err != nil { |
||||
log.Fatalln(err) |
||||
} else if all { |
||||
nodes, err = client.NodeList(cmd.Context(), runtimes.SelectedRuntime) |
||||
if err != nil { |
||||
log.Fatalln(err) |
||||
} |
||||
return nodes |
||||
} |
||||
|
||||
if len(args) < 1 { |
||||
log.Fatalln("Expecting at least one registry name if `--all` is not set") |
||||
} |
||||
|
||||
for _, name := range args { |
||||
node, err := client.NodeGet(cmd.Context(), runtimes.SelectedRuntime, &k3d.Node{Name: name}) |
||||
if err != nil { |
||||
log.Fatalln(err) |
||||
} |
||||
nodes = append(nodes, node) |
||||
} |
||||
|
||||
return nodes |
||||
} |
@ -0,0 +1,112 @@ |
||||
/* |
||||
Copyright © 2020 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 registry |
||||
|
||||
import ( |
||||
"fmt" |
||||
"strings" |
||||
|
||||
"github.com/liggitt/tabwriter" |
||||
"github.com/rancher/k3d/v4/cmd/util" |
||||
"github.com/rancher/k3d/v4/pkg/client" |
||||
"github.com/rancher/k3d/v4/pkg/runtimes" |
||||
k3d "github.com/rancher/k3d/v4/pkg/types" |
||||
log "github.com/sirupsen/logrus" |
||||
"github.com/spf13/cobra" |
||||
) |
||||
|
||||
type registryListFlags struct { |
||||
noHeader bool |
||||
output string |
||||
} |
||||
|
||||
// NewCmdRegistryList creates a new cobra command
|
||||
func NewCmdRegistryList() *cobra.Command { |
||||
registryListFlags := registryListFlags{} |
||||
|
||||
// create new command
|
||||
cmd := &cobra.Command{ |
||||
Use: "list [NAME [NAME...]]", |
||||
Aliases: []string{"ls", "get"}, |
||||
Short: "List registries", |
||||
Long: `List registries.`, |
||||
Args: cobra.MinimumNArgs(0), // 0 or more; 0 = all
|
||||
ValidArgsFunction: util.ValidArgsAvailableRegistries, |
||||
Run: func(cmd *cobra.Command, args []string) { |
||||
var existingNodes []*k3d.Node |
||||
|
||||
nodes := []*k3d.Node{} |
||||
for _, name := range args { |
||||
nodes = append(nodes, &k3d.Node{ |
||||
Name: name, |
||||
}) |
||||
} |
||||
|
||||
if len(nodes) == 0 { // Option a) no name specified -> get all registries
|
||||
found, err := client.NodeList(cmd.Context(), runtimes.SelectedRuntime) |
||||
if err != nil { |
||||
log.Fatalln(err) |
||||
} |
||||
existingNodes = append(existingNodes, found...) |
||||
} else { // Option b) registry name(s) specified -> get specific registries
|
||||
for _, node := range nodes { |
||||
log.Tracef("Node %s", node.Name) |
||||
found, err := client.NodeGet(cmd.Context(), runtimes.SelectedRuntime, node) |
||||
if err != nil { |
||||
log.Fatalln(err) |
||||
} |
||||
existingNodes = append(existingNodes, found) |
||||
} |
||||
} |
||||
existingNodes = client.NodeFilterByRoles(existingNodes, []k3d.Role{k3d.RegistryRole}, []k3d.Role{}) |
||||
|
||||
// print existing registries
|
||||
headers := &[]string{} |
||||
if !registryListFlags.noHeader { |
||||
headers = &[]string{"NAME", "ROLE", "CLUSTER"} // TODO: add status
|
||||
} |
||||
|
||||
util.PrintNodes(existingNodes, registryListFlags.output, |
||||
headers, util.NodePrinterFunc(func(tabwriter *tabwriter.Writer, node *k3d.Node) { |
||||
cluster := "*" |
||||
if _, ok := node.Labels[k3d.LabelClusterName]; ok { |
||||
cluster = node.Labels[k3d.LabelClusterName] |
||||
} |
||||
fmt.Fprintf(tabwriter, "%s\t%s\t%s\n", |
||||
strings.TrimPrefix(node.Name, "/"), |
||||
string(node.Role), |
||||
cluster, |
||||
) |
||||
}), |
||||
) |
||||
}, |
||||
} |
||||
|
||||
// add flags
|
||||
cmd.Flags().BoolVar(®istryListFlags.noHeader, "no-headers", false, "Disable headers") |
||||
cmd.Flags().StringVarP(®istryListFlags.output, "output", "o", "", "Output format. One of: json|yaml") |
||||
|
||||
// add subcommands
|
||||
|
||||
// done
|
||||
return cmd |
||||
} |
@ -0,0 +1,29 @@ |
||||
/* |
||||
Copyright © 2020 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 registry |
||||
|
||||
import "github.com/spf13/cobra" |
||||
|
||||
// NewCmdRegistryStart creates a new cobra command
|
||||
func NewCmdRegistryStart() *cobra.Command { |
||||
return &cobra.Command{} |
||||
} |
@ -0,0 +1,29 @@ |
||||
/* |
||||
Copyright © 2020 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 registry |
||||
|
||||
import "github.com/spf13/cobra" |
||||
|
||||
// NewCmdRegistryStop creates a new cobra command
|
||||
func NewCmdRegistryStop() *cobra.Command { |
||||
return &cobra.Command{} |
||||
} |
@ -0,0 +1,89 @@ |
||||
/* |
||||
Copyright © 2020 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 ( |
||||
"encoding/json" |
||||
"fmt" |
||||
"os" |
||||
"sort" |
||||
"strings" |
||||
|
||||
"github.com/liggitt/tabwriter" |
||||
k3d "github.com/rancher/k3d/v4/pkg/types" |
||||
log "github.com/sirupsen/logrus" |
||||
"gopkg.in/yaml.v2" |
||||
) |
||||
|
||||
type NodePrinter interface { |
||||
Print(*tabwriter.Writer, *k3d.Node) |
||||
} |
||||
|
||||
type NodePrinterFunc func(*tabwriter.Writer, *k3d.Node) |
||||
|
||||
func (npf NodePrinterFunc) Print(writter *tabwriter.Writer, node *k3d.Node) { |
||||
npf(writter, node) |
||||
} |
||||
|
||||
// PrintNodes prints a list of nodes, either as a table or as a JSON/YAML listing
|
||||
func PrintNodes(nodes []*k3d.Node, outputFormat string, headers *[]string, nodePrinter NodePrinter) { |
||||
outputFormat = strings.ToLower(outputFormat) |
||||
|
||||
tabwriter := tabwriter.NewWriter(os.Stdout, 6, 4, 3, ' ', tabwriter.RememberWidths) |
||||
defer tabwriter.Flush() |
||||
|
||||
if outputFormat != "json" && outputFormat != "yaml" { |
||||
if headers != nil { |
||||
_, err := fmt.Fprintf(tabwriter, "%s\n", strings.Join(*headers, "\t")) |
||||
if err != nil { |
||||
log.Fatalln("Failed to print headers") |
||||
} |
||||
} |
||||
} |
||||
|
||||
sort.Slice(nodes, func(i, j int) bool { |
||||
return nodes[i].Name < nodes[j].Name |
||||
}) |
||||
|
||||
if outputFormat == "json" || outputFormat == "yaml" { |
||||
var b []byte |
||||
var err error |
||||
|
||||
switch outputFormat { |
||||
case "json": |
||||
b, err = json.Marshal(nodes) |
||||
case "yaml": |
||||
b, err = yaml.Marshal(nodes) |
||||
} |
||||
if err != nil { |
||||
fmt.Println(err) |
||||
return |
||||
} |
||||
fmt.Println(string(b)) |
||||
} else { |
||||
for _, node := range nodes { |
||||
if !(outputFormat == "json" || outputFormat == "yaml") { |
||||
nodePrinter.Print(tabwriter, node) |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,12 +1,15 @@ |
||||
# Defaults |
||||
|
||||
- multiple server nodes |
||||
- by default, when `--server` > 1 and no `--datastore-x` option is set, the first server node (server-0) will be the initializing server node |
||||
- the initializing server node will have the `--cluster-init` flag appended |
||||
- all other server nodes will refer to the initializing server node via `--server https://<init-node>:6443` |
||||
- by default, when `--server` > 1 and no `--datastore-x` option is set, the first server node (server-0) will be the initializing server node |
||||
- the initializing server node will have the `--cluster-init` flag appended |
||||
- all other server nodes will refer to the initializing server node via `--server https://<init-node>:6443` |
||||
- API-Ports |
||||
- by default, we don't expose any API-Port (no host port mapping) |
||||
- by default, we expose the API-Port (`6443`) by forwarding traffic from the default server loadbalancer (nginx container) to the server node(s) |
||||
- port `6443` of the loadbalancer is then mapped to a specific (`--api-port` flag) or a random (default) port on the host system |
||||
- kubeconfig |
||||
- if `--[update|merge]-default-kubeconfig` is set, we use the default loading rules to get the default kubeconfig: |
||||
- First: kubeconfig specified via the KUBECONFIG environment variable (error out if multiple are specified) |
||||
- Second: default kubeconfig in home directory (e.g. `$HOME/.kube/config`) |
||||
- if `--kubeconfig-update-default` is set, we use the default loading rules to get the default kubeconfig: |
||||
- First: kubeconfig specified via the KUBECONFIG environment variable (error out if multiple are specified) |
||||
- Second: default kubeconfig in home directory (e.g. `$HOME/.kube/config`) |
||||
- Networking |
||||
- [by default, k3d creates a new (docker) network for every cluster](./networking) |
||||
|
@ -1,46 +1,49 @@ |
||||
module github.com/rancher/k3d/v3 |
||||
module github.com/rancher/k3d/v4 |
||||
|
||||
go 1.14 |
||||
go 1.15 |
||||
|
||||
require ( |
||||
github.com/Microsoft/hcsshim v0.8.9 // indirect |
||||
github.com/Microsoft/hcsshim/test v0.0.0-20201030212021-6e6b6ce98037 // indirect |
||||
github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102 // indirect |
||||
github.com/Microsoft/go-winio v0.4.15 // indirect |
||||
github.com/Microsoft/hcsshim v0.8.10 // indirect |
||||
github.com/Microsoft/hcsshim/test v0.0.0-20201202232227-2010d9a3eeb0 // indirect |
||||
github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327 // indirect |
||||
github.com/containerd/containerd v1.4.1 |
||||
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6 // indirect |
||||
github.com/containerd/fifo v0.0.0-20190816180239-bda0ff6ed73c // indirect |
||||
github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8 // indirect |
||||
github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd // indirect |
||||
github.com/containerd/continuity v0.0.0-20201204194424-b0f312dbb49a // indirect |
||||
github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c // indirect |
||||
github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0 // indirect |
||||
github.com/containerd/ttrpc v1.0.2 // indirect |
||||
github.com/containerd/typeurl v1.0.1 // indirect |
||||
github.com/docker/cli v20.10.0-beta1.0.20201103165149-c20be83d6b34+incompatible |
||||
github.com/docker/distribution v0.0.0-20201029003056-f5cdc24dd3d8 // indirect |
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200528204242-89382f2f2074+incompatible |
||||
github.com/docker/docker v20.10.0+incompatible |
||||
github.com/docker/go-connections v0.4.0 |
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect |
||||
github.com/go-test/deep v1.0.4 |
||||
github.com/gogo/googleapis v1.3.0 // indirect |
||||
github.com/golang/protobuf v1.4.3 // indirect |
||||
github.com/gogo/googleapis v1.4.0 // indirect |
||||
github.com/heroku/docker-registry-client v0.0.0-20190909225348-afc9e1acc3d5 |
||||
github.com/imdario/mergo v0.3.9 |
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de |
||||
github.com/mitchellh/go-homedir v1.1.0 |
||||
github.com/moby/sys/mount v0.1.0 // indirect |
||||
github.com/moby/term v0.0.0-20200507201656-73f35e472e8f // indirect |
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect |
||||
github.com/mitchellh/mapstructure v1.3.3 // indirect |
||||
github.com/moby/sys/mount v0.2.0 // indirect |
||||
github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf // indirect |
||||
github.com/morikuni/aec v1.0.0 // indirect |
||||
github.com/opencontainers/image-spec v1.0.1 // indirect |
||||
github.com/opencontainers/runc v0.1.1 // indirect |
||||
github.com/opencontainers/selinux v1.6.0 // indirect |
||||
github.com/pelletier/go-toml v1.8.0 // indirect |
||||
github.com/sirupsen/logrus v1.7.0 |
||||
github.com/spf13/afero v1.3.4 // indirect |
||||
github.com/spf13/cast v1.3.1 // indirect |
||||
github.com/spf13/cobra v1.1.0 |
||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 // indirect |
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 // indirect |
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect |
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect |
||||
github.com/spf13/viper v1.7.1 |
||||
github.com/stretchr/testify v1.6.1 // indirect |
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect |
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 |
||||
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 // indirect |
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect |
||||
google.golang.org/genproto v0.0.0-20201103154000-415bd0cd5df6 // indirect |
||||
google.golang.org/grpc v1.33.1 // indirect |
||||
google.golang.org/protobuf v1.25.0 // indirect |
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect |
||||
google.golang.org/grpc v1.34.0 // indirect |
||||
gopkg.in/ini.v1 v1.58.0 // indirect |
||||
gopkg.in/yaml.v2 v2.3.0 |
||||
gotest.tools/v3 v3.0.2 // indirect |
||||
gotest.tools v2.2.0+incompatible |
||||
gotest.tools/v3 v3.0.3 // indirect |
||||
k8s.io/client-go v0.17.0 |
||||
k8s.io/utils v0.0.0-20200109141947-94aeca20bf09 // indirect |
||||
) |
||||
|
@ -0,0 +1,39 @@ |
||||
/* |
||||
Copyright © 2020 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 actions |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
"github.com/rancher/k3d/v4/pkg/runtimes" |
||||
k3d "github.com/rancher/k3d/v4/pkg/types" |
||||
) |
||||
|
||||
type WriteFileAction struct { |
||||
Runtime runtimes.Runtime |
||||
Content []byte |
||||
Dest string |
||||
} |
||||
|
||||
func (act WriteFileAction) Run(ctx context.Context, node *k3d.Node) error { |
||||
return act.Runtime.WriteToNode(ctx, act.Content, act.Dest, node) |
||||
} |
@ -0,0 +1,306 @@ |
||||
/* |
||||
Copyright © 2020 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 client |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
|
||||
"github.com/docker/go-connections/nat" |
||||
"github.com/rancher/k3d/v4/pkg/runtimes" |
||||
k3d "github.com/rancher/k3d/v4/pkg/types" |
||||
"github.com/rancher/k3d/v4/pkg/types/k3s" |
||||
"github.com/rancher/k3d/v4/pkg/types/k8s" |
||||
log "github.com/sirupsen/logrus" |
||||
"gopkg.in/yaml.v2" |
||||
) |
||||
|
||||
func RegistryRun(ctx context.Context, runtime runtimes.Runtime, reg *k3d.Registry) (*k3d.Node, error) { |
||||
regNode, err := RegistryCreate(ctx, runtime, reg) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("Failed to create registry: %+v", err) |
||||
} |
||||
|
||||
if err := NodeStart(ctx, runtime, regNode, k3d.NodeStartOpts{}); err != nil { |
||||
return nil, fmt.Errorf("Failed to start registry: %+v", err) |
||||
} |
||||
|
||||
return regNode, err |
||||
} |
||||
|
||||
// RegistryCreate creates a registry node
|
||||
func RegistryCreate(ctx context.Context, runtime runtimes.Runtime, reg *k3d.Registry) (*k3d.Node, error) { |
||||
|
||||
// registry name
|
||||
if len(reg.Host) == 0 { |
||||
reg.Host = k3d.DefaultRegistryName |
||||
} |
||||
// if err := ValidateHostname(reg.Host); err != nil {
|
||||
// log.Errorln("Invalid name for registry")
|
||||
// log.Fatalln(err)
|
||||
// }
|
||||
|
||||
registryNode := &k3d.Node{ |
||||
Name: reg.Host, |
||||
Image: reg.Image, |
||||
Role: k3d.RegistryRole, |
||||
Network: "bridge", // Default network: TODO: change to const from types
|
||||
} |
||||
|
||||
// error out if that registry exists already
|
||||
existingNode, err := runtime.GetNode(ctx, registryNode) |
||||
if err == nil && existingNode != nil { |
||||
return nil, fmt.Errorf("A registry node with that name already exists") |
||||
} |
||||
|
||||
// setup the node labels
|
||||
registryNode.Labels = map[string]string{ |
||||
k3d.LabelRole: string(k3d.RegistryRole), |
||||
k3d.LabelRegistryHost: reg.ExposureOpts.Host, // TODO: docker machine host?
|
||||
k3d.LabelRegistryHostIP: reg.ExposureOpts.Binding.HostIP, |
||||
k3d.LabelRegistryPortExternal: reg.ExposureOpts.Binding.HostPort, |
||||
k3d.LabelRegistryPortInternal: reg.ExposureOpts.Port.Port(), |
||||
} |
||||
for k, v := range k3d.DefaultObjectLabels { |
||||
registryNode.Labels[k] = v |
||||
} |
||||
|
||||
// port
|
||||
registryNode.Ports = nat.PortMap{} |
||||
registryNode.Ports[reg.ExposureOpts.Port] = []nat.PortBinding{reg.ExposureOpts.Binding} |
||||
|
||||
// create the registry node
|
||||
log.Infof("Creating node '%s'", registryNode.Name) |
||||
if err := NodeCreate(ctx, runtime, registryNode, k3d.NodeCreateOpts{}); err != nil { |
||||
log.Errorln("Failed to create registry node") |
||||
return nil, err |
||||
} |
||||
|
||||
log.Infof("Successfully created registry '%s'", registryNode.Name) |
||||
|
||||
return registryNode, nil |
||||
|
||||
} |
||||
|
||||
// RegistryConnectClusters connects an existing registry to one or more clusters
|
||||
func RegistryConnectClusters(ctx context.Context, runtime runtimes.Runtime, registryNode *k3d.Node, clusters []*k3d.Cluster) error { |
||||
|
||||
// find registry node
|
||||
registryNode, err := NodeGet(ctx, runtime, registryNode) |
||||
if err != nil { |
||||
log.Errorf("Failed to find registry node '%s'", registryNode.Name) |
||||
return err |
||||
} |
||||
|
||||
// get cluster details and connect
|
||||
failed := 0 |
||||
for _, c := range clusters { |
||||
cluster, err := ClusterGet(ctx, runtime, c) |
||||
if err != nil { |
||||
log.Warnf("Failed to connect to cluster '%s': Cluster not found", c.Name) |
||||
failed++ |
||||
continue |
||||
} |
||||
if err := runtime.ConnectNodeToNetwork(ctx, registryNode, cluster.Network.Name); err != nil { |
||||
log.Warnf("Failed to connect to cluster '%s': Connection failed", cluster.Name) |
||||
log.Warnln(err) |
||||
failed++ |
||||
} |
||||
} |
||||
|
||||
if failed > 0 { |
||||
return fmt.Errorf("Failed to connect to one or more clusters") |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// RegistryConnectNetworks connects an existing registry to one or more networks
|
||||
func RegistryConnectNetworks(ctx context.Context, runtime runtimes.Runtime, registryNode *k3d.Node, networks []string) error { |
||||
|
||||
// find registry node
|
||||
registryNode, err := NodeGet(ctx, runtime, registryNode) |
||||
if err != nil { |
||||
log.Errorf("Failed to find registry node '%s'", registryNode.Name) |
||||
return err |
||||
} |
||||
|
||||
// get cluster details and connect
|
||||
failed := 0 |
||||
for _, net := range networks { |
||||
if err := runtime.ConnectNodeToNetwork(ctx, registryNode, net); err != nil { |
||||
log.Warnf("Failed to connect to network '%s': Connection failed", net) |
||||
log.Warnln(err) |
||||
failed++ |
||||
} |
||||
} |
||||
|
||||
if failed > 0 { |
||||
return fmt.Errorf("Failed to connect to one or more networks") |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// RegistryGenerateK3sConfig generates the k3s specific registries.yaml configuration for multiple registries
|
||||
func RegistryGenerateK3sConfig(ctx context.Context, registries []*k3d.Registry) (*k3s.Registry, error) { |
||||
regConf := &k3s.Registry{} |
||||
|
||||
for _, reg := range registries { |
||||
internalAddress := fmt.Sprintf("%s:%s", reg.Host, reg.ExposureOpts.Port.Port()) |
||||
externalAddress := fmt.Sprintf("%s:%s", reg.Host, reg.ExposureOpts.Binding.HostPort) |
||||
|
||||
// init mirrors if nil
|
||||
if regConf.Mirrors == nil { |
||||
regConf.Mirrors = make(map[string]k3s.Mirror) |
||||
} |
||||
|
||||
regConf.Mirrors[externalAddress] = k3s.Mirror{ |
||||
Endpoints: []string{ |
||||
fmt.Sprintf("http://%s", internalAddress), |
||||
}, |
||||
} |
||||
|
||||
if reg.Options.Proxy.RemoteURL != "" { |
||||
regConf.Mirrors[reg.Options.Proxy.RemoteURL] = k3s.Mirror{ |
||||
Endpoints: []string{fmt.Sprintf("http://%s", internalAddress)}, |
||||
} |
||||
} |
||||
} |
||||
|
||||
return regConf, nil |
||||
} |
||||
|
||||
// RegistryGet gets a registry node by name and returns it as a registry object
|
||||
func RegistryGet(ctx context.Context, runtime runtimes.Runtime, name string) (*k3d.Registry, error) { |
||||
regNode, err := runtime.GetNode(ctx, &k3d.Node{ |
||||
Name: name, |
||||
Role: k3d.RegistryRole, |
||||
}) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("Failed to find registry '%s': %+v", name, err) |
||||
} |
||||
|
||||
registry := &k3d.Registry{ |
||||
Host: regNode.Name, |
||||
} |
||||
// TODO: finish RegistryGet
|
||||
return registry, nil |
||||
|
||||
} |
||||
|
||||
// RegistryFromNode transforms a node spec to a registry spec
|
||||
func RegistryFromNode(node *k3d.Node) (*k3d.Registry, error) { |
||||
registry := &k3d.Registry{ |
||||
Host: node.Name, |
||||
Image: node.Image, |
||||
} |
||||
|
||||
// we expect exactly one portmap
|
||||
if len(node.Ports) != 1 { |
||||
return nil, fmt.Errorf("Failed to parse registry spec from node %+v: 0 or multiple ports defined, where one is expected", node) |
||||
} |
||||
|
||||
for port, bindings := range node.Ports { |
||||
registry.ExposureOpts.Port = port |
||||
|
||||
// we expect 0 or 1 binding for that port
|
||||
if len(bindings) > 1 { |
||||
return nil, fmt.Errorf("Failed to parse registry spec from node %+v: Multiple bindings '%+v' specified for port '%s' where one is expected", node, bindings, port) |
||||
} |
||||
|
||||
for _, binding := range bindings { |
||||
registry.ExposureOpts.Binding = binding |
||||
} |
||||
} |
||||
|
||||
log.Tracef("Got registry %+v from node %+v", registry, node) |
||||
|
||||
return registry, nil |
||||
|
||||
} |
||||
|
||||
// RegistryGenerateLocalRegistryHostingConfigMapYAML generates a ConfigMap used to advertise the registries in the cluster
|
||||
func RegistryGenerateLocalRegistryHostingConfigMapYAML(ctx context.Context, registries []*k3d.Registry) ([]byte, error) { |
||||
|
||||
type cmMetadata struct { |
||||
Name string `yaml:"name"` |
||||
Namespace string `yaml:"namespace"` |
||||
} |
||||
|
||||
type cmData struct { |
||||
RegHostV1 string `yaml:"localRegistryHosting.v1"` |
||||
} |
||||
|
||||
type configmap struct { |
||||
APIVersion string `yaml:"apiVersion"` |
||||
Kind string `yaml:"kind"` |
||||
Metadata cmMetadata `yaml:"metadata"` |
||||
Data cmData `yaml:"data"` |
||||
} |
||||
|
||||
if len(registries) > 1 { |
||||
log.Warnf("More than one registry specified, but the LocalRegistryHostingV1 spec only supports one -> Selecting the first one: %s", registries[0].Host) |
||||
} |
||||
|
||||
if len(registries) < 1 { |
||||
log.Debugln("No registry specified, not generating local registry hosting configmap") |
||||
return nil, nil |
||||
} |
||||
|
||||
host := registries[0].ExposureOpts.Host |
||||
if host == "" { |
||||
host = registries[0].ExposureOpts.Binding.HostIP |
||||
} |
||||
|
||||
dat, err := yaml.Marshal( |
||||
k8s.LocalRegistryHostingV1{ |
||||
Host: fmt.Sprintf("%s:%s", host, registries[0].ExposureOpts.Binding.HostPort), |
||||
HostFromContainerRuntime: fmt.Sprintf("%s:%s", registries[0].Host, registries[0].ExposureOpts.Port.Port()), |
||||
Help: "https://k3d.io/usage/guides/registries/#using-a-local-registry", |
||||
}, |
||||
) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
cm := configmap{ |
||||
APIVersion: "v1", |
||||
Kind: "ConfigMap", |
||||
Metadata: cmMetadata{ |
||||
Name: "local-registry-hosting", |
||||
Namespace: "kube-public", |
||||
}, |
||||
Data: cmData{ |
||||
RegHostV1: string(dat), |
||||
}, |
||||
} |
||||
|
||||
cmYaml, err := yaml.Marshal(cm) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
log.Tracef("LocalRegistryHostingConfigMapYaml: %s", string(cmYaml)) |
||||
|
||||
return cmYaml, nil |
||||
} |
@ -0,0 +1,66 @@ |
||||
/* |
||||
Copyright © 2020 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 client |
||||
|
||||
import ( |
||||
"context" |
||||
"strings" |
||||
"testing" |
||||
|
||||
"github.com/docker/go-connections/nat" |
||||
k3d "github.com/rancher/k3d/v4/pkg/types" |
||||
) |
||||
|
||||
func TestRegistryGenerateLocalRegistryHostingConfigMapYAML(t *testing.T) { |
||||
var err error |
||||
|
||||
expectedYAMLString := `apiVersion: v1 |
||||
kind: ConfigMap |
||||
metadata: |
||||
name: local-registry-hosting |
||||
namespace: kube-public |
||||
data: |
||||
localRegistryHosting.v1: | |
||||
host: test-host:5432 |
||||
hostFromContainerRuntime: test-host:1234 |
||||
help: https://k3d.io/usage/guides/registries/#using-a-local-registry
|
||||
` |
||||
|
||||
reg := &k3d.Registry{ |
||||
Host: "test-host", |
||||
} |
||||
reg.ExposureOpts.Host = "test-host" |
||||
reg.ExposureOpts.Port = nat.Port("1234/tcp") |
||||
reg.ExposureOpts.Binding.HostPort = "5432" |
||||
|
||||
regs := []*k3d.Registry{reg} |
||||
|
||||
cm, err := RegistryGenerateLocalRegistryHostingConfigMapYAML(context.Background(), regs) |
||||
if err != nil { |
||||
t.Error(err) |
||||
} |
||||
|
||||
if !(strings.TrimSpace(string(cm)) == strings.TrimSpace(expectedYAMLString)) { |
||||
t.Errorf("Computed configmap\n-> Actual: %s\n does not match expected YAML\n-> Expected: %s", strings.TrimSpace(string(cm)), strings.TrimSpace(expectedYAMLString)) |
||||
} |
||||
|
||||
} |
@ -0,0 +1 @@ |
||||
{"host":"test-host:5432","hostFromContainerRuntime":"test-host:1234","help":"https://k3d.io/usage/guides/registries/#using-a-local-registry"} |
@ -0,0 +1,21 @@ |
||||
apiVersion: apps/v1 |
||||
kind: Deployment |
||||
metadata: |
||||
name: nginx-test-registry |
||||
labels: |
||||
app: nginx-test-registry |
||||
spec: |
||||
replicas: 1 |
||||
selector: |
||||
matchLabels: |
||||
app: nginx-test-registry |
||||
template: |
||||
metadata: |
||||
labels: |
||||
app: nginx-test-registry |
||||
spec: |
||||
containers: |
||||
- name: nginx-test-registry |
||||
image: k3d-newreg/alpine:test |
||||
ports: |
||||
- containerPort: 80 |
@ -0,0 +1,83 @@ |
||||
/* |
||||
Copyright © 2020 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 config |
||||
|
||||
import ( |
||||
"fmt" |
||||
"strings" |
||||
|
||||
log "github.com/sirupsen/logrus" |
||||
|
||||
"github.com/spf13/viper" |
||||
|
||||
conf "github.com/rancher/k3d/v4/pkg/config/v1alpha1" |
||||
k3d "github.com/rancher/k3d/v4/pkg/types" |
||||
) |
||||
|
||||
func ReadConfig(file string) (conf.Config, error) { |
||||
cfgViper := viper.New() |
||||
|
||||
cfgViper.SetConfigFile(file) |
||||
|
||||
cfgViper.SetConfigType("yaml") |
||||
cfgViper.SetEnvPrefix(k3d.DefaultObjectNamePrefix) |
||||
cfgViper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) |
||||
cfgViper.AutomaticEnv() |
||||
|
||||
// try to read config into memory (viper map structure)
|
||||
if err := cfgViper.ReadInConfig(); err != nil { |
||||
if _, ok := err.(viper.ConfigFileNotFoundError); ok { |
||||
log.Errorln("No config file found!") |
||||
|
||||
return nil, err |
||||
} |
||||
// config file found but some other error happened
|
||||
log.Debugf("Failed to read config file: %+v", cfgViper.ConfigFileUsed()) |
||||
return nil, err |
||||
} |
||||
|
||||
var cfg conf.Config |
||||
|
||||
// determine config kind
|
||||
switch strings.ToLower(cfgViper.GetString("kind")) { |
||||
case "simple": |
||||
cfg = conf.SimpleConfig{} |
||||
case "cluster": |
||||
cfg = conf.ClusterConfig{} |
||||
case "clusterlist": |
||||
cfg = conf.ClusterListConfig{} |
||||
case "": |
||||
return nil, fmt.Errorf("Missing `kind` in config file") |
||||
default: |
||||
return nil, fmt.Errorf("Unknown `kind` '%s' in config file", cfgViper.GetString("kind")) |
||||
} |
||||
|
||||
if err := cfgViper.Unmarshal(&cfg); err != nil { |
||||
log.Errorln("Failed to unmarshal File config") |
||||
|
||||
return nil, err |
||||
} |
||||
|
||||
log.Infof("Using Config: %s", cfgViper.ConfigFileUsed()) |
||||
|
||||
return cfg, nil |
||||
} |
@ -0,0 +1,201 @@ |
||||
/* |
||||
Copyright © 2020 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 config |
||||
|
||||
import ( |
||||
"testing" |
||||
"time" |
||||
|
||||
"github.com/go-test/deep" |
||||
conf "github.com/rancher/k3d/v4/pkg/config/v1alpha1" |
||||
|
||||
k3d "github.com/rancher/k3d/v4/pkg/types" |
||||
) |
||||
|
||||
func TestReadSimpleConfig(t *testing.T) { |
||||
|
||||
exposedAPI := conf.SimpleExposureOpts{} |
||||
exposedAPI.HostIP = "0.0.0.0" |
||||
exposedAPI.HostPort = "6443" |
||||
|
||||
expectedConfig := conf.SimpleConfig{ |
||||
TypeMeta: conf.TypeMeta{ |
||||
APIVersion: "k3d.io/v1alpha1", |
||||
Kind: "Simple", |
||||
}, |
||||
Name: "test", |
||||
Servers: 1, |
||||
Agents: 2, |
||||
ExposeAPI: exposedAPI, |
||||
Image: "rancher/k3s:latest", |
||||
Volumes: []conf.VolumeWithNodeFilters{ |
||||
{ |
||||
Volume: "/my/path:/some/path", |
||||
NodeFilters: []string{"all"}, |
||||
}, |
||||
}, |
||||
Ports: []conf.PortWithNodeFilters{ |
||||
{ |
||||
Port: "80:80", |
||||
NodeFilters: []string{"loadbalancer"}, |
||||
}, { |
||||
Port: "0.0.0.0:443:443", |
||||
NodeFilters: []string{"loadbalancer"}, |
||||
}, |
||||
}, |
||||
Labels: []conf.LabelWithNodeFilters{ |
||||
{ |
||||
Label: "foo=bar", |
||||
NodeFilters: []string{"server[0]", "loadbalancer"}, |
||||
}, |
||||
}, |
||||
Env: []conf.EnvVarWithNodeFilters{ |
||||
{ |
||||
EnvVar: "bar=baz", |
||||
NodeFilters: []string{"all"}, |
||||
}, |
||||
}, |
||||
Options: conf.SimpleConfigOptions{ |
||||
K3dOptions: conf.SimpleConfigOptionsK3d{ |
||||
Wait: true, |
||||
Timeout: 60 * time.Second, |
||||
DisableLoadbalancer: false, |
||||
DisableImageVolume: false, |
||||
}, |
||||
K3sOptions: conf.SimpleConfigOptionsK3s{ |
||||
ExtraServerArgs: []string{"--tls-san=127.0.0.1"}, |
||||
ExtraAgentArgs: []string{}, |
||||
}, |
||||
KubeconfigOptions: conf.SimpleConfigOptionsKubeconfig{ |
||||
UpdateDefaultKubeconfig: true, |
||||
SwitchCurrentContext: true, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
cfgFile := "./test_assets/config_test_simple.yaml" |
||||
|
||||
cfg, err := ReadConfig(cfgFile) |
||||
if err != nil { |
||||
t.Error(err) |
||||
} |
||||
|
||||
simpleCfg, ok := cfg.(conf.SimpleConfig) |
||||
if !ok { |
||||
t.Error("Config is not of type SimpleConfig") |
||||
} |
||||
|
||||
t.Logf("\n========== Read Config ==========\n%+v\n=================================\n", simpleCfg) |
||||
|
||||
if diff := deep.Equal(simpleCfg, expectedConfig); diff != nil { |
||||
t.Errorf("Actual representation\n%+v\ndoes not match expected representation\n%+v\nDiff:\n%+v", simpleCfg, expectedConfig, diff) |
||||
} |
||||
|
||||
} |
||||
|
||||
func TestReadClusterConfig(t *testing.T) { |
||||
|
||||
expectedConfig := conf.ClusterConfig{ |
||||
TypeMeta: conf.TypeMeta{ |
||||
APIVersion: "k3d.io/v1alpha1", |
||||
Kind: "Cluster", |
||||
}, |
||||
Cluster: k3d.Cluster{ |
||||
Name: "foo", |
||||
Nodes: []*k3d.Node{ |
||||
{ |
||||
Name: "foo-node-0", |
||||
Role: k3d.ServerRole, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
cfgFile := "./test_assets/config_test_cluster.yaml" |
||||
|
||||
readConfig, err := ReadConfig(cfgFile) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
|
||||
t.Logf("\n========== Read Config ==========\n%+v\n=================================\n", readConfig) |
||||
|
||||
if diff := deep.Equal(readConfig, expectedConfig); diff != nil { |
||||
t.Errorf("Actual representation\n%+v\ndoes not match expected representation\n%+v\nDiff:\n%+v", readConfig, expectedConfig, diff) |
||||
} |
||||
|
||||
} |
||||
|
||||
func TestReadClusterListConfig(t *testing.T) { |
||||
|
||||
expectedConfig := conf.ClusterListConfig{ |
||||
TypeMeta: conf.TypeMeta{ |
||||
APIVersion: "k3d.io/v1alpha1", |
||||
Kind: "ClusterList", |
||||
}, |
||||
Clusters: []k3d.Cluster{ |
||||
{ |
||||
Name: "foo", |
||||
Nodes: []*k3d.Node{ |
||||
{ |
||||
Name: "foo-node-0", |
||||
Role: k3d.ServerRole, |
||||
}, |
||||
}, |
||||
}, |
||||
{ |
||||
Name: "bar", |
||||
Nodes: []*k3d.Node{ |
||||
{ |
||||
Name: "bar-node-0", |
||||
Role: k3d.ServerRole, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
cfgFile := "./test_assets/config_test_cluster_list.yaml" |
||||
|
||||
readConfig, err := ReadConfig(cfgFile) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
|
||||
t.Logf("\n========== Read Config ==========\n%+v\n=================================\n", readConfig) |
||||
|
||||
if diff := deep.Equal(readConfig, expectedConfig); diff != nil { |
||||
t.Errorf("Actual representation\n%+v\ndoes not match expected representation\n%+v\nDiff:\n%+v", readConfig, expectedConfig, diff) |
||||
} |
||||
|
||||
} |
||||
|
||||
func TestReadUnknownConfig(t *testing.T) { |
||||
|
||||
cfgFile := "./test_assets/config_test_unknown.yaml" |
||||
|
||||
_, err := ReadConfig(cfgFile) |
||||
if err == nil { |
||||
t.Fail() |
||||
} |
||||
|
||||
} |
@ -0,0 +1,42 @@ |
||||
/* |
||||
Copyright © 2020 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 config |
||||
|
||||
import ( |
||||
"github.com/imdario/mergo" |
||||
conf "github.com/rancher/k3d/v4/pkg/config/v1alpha1" |
||||
log "github.com/sirupsen/logrus" |
||||
) |
||||
|
||||
// MergeSimple merges two simple configuration files with the values of the destination one having priority
|
||||
func MergeSimple(dest, src conf.SimpleConfig) (*conf.SimpleConfig, error) { |
||||
log.Debugf("Merging %+v into %+v", src, dest) |
||||
|
||||
if err := mergo.Merge(&dest, src); err != nil { |
||||
log.Errorln("Failed to merge config") |
||||
|
||||
return nil, err |
||||
} |
||||
|
||||
return &dest, nil |
||||
} |
@ -0,0 +1,59 @@ |
||||
/* |
||||
Copyright © 2020 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 config |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
conf "github.com/rancher/k3d/v4/pkg/config/v1alpha1" |
||||
"gotest.tools/assert" |
||||
) |
||||
|
||||
func TestMergeSimpleConfig(t *testing.T) { |
||||
srcConfig := "./test_assets/config_test_simple.yaml" |
||||
destConfig := "./test_assets/config_test_simple_2.yaml" |
||||
|
||||
var src, dest conf.Config |
||||
var err error |
||||
|
||||
if src, err = ReadConfig(srcConfig); err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
|
||||
if dest, err = ReadConfig(destConfig); err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
|
||||
mergedConfig, err := MergeSimple(dest.(conf.SimpleConfig), src.(conf.SimpleConfig)) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
|
||||
// ensure that we get the two filled fields of destConfig
|
||||
assert.Equal(t, mergedConfig.Name, dest.(conf.SimpleConfig).Name) |
||||
assert.Equal(t, mergedConfig.Agents, dest.(conf.SimpleConfig).Agents) |
||||
|
||||
// ensure that we get the other fields from the srcConfig (only checking two of them here)
|
||||
assert.Equal(t, mergedConfig.Servers, src.(conf.SimpleConfig).Servers) |
||||
assert.Equal(t, mergedConfig.Image, src.(conf.SimpleConfig).Image) |
||||
} |
@ -0,0 +1,6 @@ |
||||
apiVersion: k3d.io/v1alpha1 |
||||
kind: Cluster |
||||
name: foo |
||||
nodes: |
||||
- name: foo-node-0 |
||||
role: server |
@ -0,0 +1,12 @@ |
||||
--- |
||||
apiVersion: k3d.io/v1alpha1 |
||||
kind: ClusterList |
||||
clusters: |
||||
- name: foo |
||||
nodes: |
||||
- name: foo-node-0 |
||||
role: server |
||||
- name: bar |
||||
nodes: |
||||
- name: bar-node-0 |
||||
role: server |
@ -0,0 +1,43 @@ |
||||
apiVersion: k3d.io/v1alpha1 |
||||
kind: Simple |
||||
name: test |
||||
servers: 1 |
||||
agents: 2 |
||||
kubeAPI: |
||||
hostIP: "0.0.0.0" |
||||
hostPort: "6443" |
||||
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 |
||||
nodeFilters: |
||||
- all |
||||
labels: |
||||
- label: foo=bar |
||||
nodeFilters: |
||||
- server[0] |
||||
- loadbalancer |
||||
|
||||
options: |
||||
k3d: |
||||
wait: true |
||||
timeout: "60s" |
||||
disableLoadbalancer: false |
||||
disableImageVolume: false |
||||
k3s: |
||||
extraServerArgs: |
||||
- --tls-san=127.0.0.1 |
||||
extraAgentArgs: [] |
||||
kubeconfig: |
||||
updateDefaultKubeconfig: true |
||||
switchCurrentContext: true |
@ -0,0 +1,4 @@ |
||||
apiVersion: k3d.io/v1alpha1 |
||||
kind: Simple |
||||
name: supertest |
||||
agents: 8 |
@ -0,0 +1,3 @@ |
||||
apiVersion: k3d.io/v1alpha1 |
||||
kind: Unknown |
||||
foo: bar |
@ -0,0 +1,271 @@ |
||||
/* |
||||
Copyright © 2020 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 config |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
|
||||
"github.com/docker/go-connections/nat" |
||||
cliutil "github.com/rancher/k3d/v4/cmd/util" // TODO: move parseapiport to pkg
|
||||
conf "github.com/rancher/k3d/v4/pkg/config/v1alpha1" |
||||
"github.com/rancher/k3d/v4/pkg/runtimes" |
||||
k3d "github.com/rancher/k3d/v4/pkg/types" |
||||
"github.com/rancher/k3d/v4/pkg/util" |
||||
"github.com/rancher/k3d/v4/version" |
||||
|
||||
log "github.com/sirupsen/logrus" |
||||
) |
||||
|
||||
// TransformSimpleToClusterConfig transforms a simple configuration to a full-fledged cluster configuration
|
||||
func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtime, simpleConfig conf.SimpleConfig) (*conf.ClusterConfig, error) { |
||||
|
||||
// set default cluster name
|
||||
if simpleConfig.Name == "" { |
||||
simpleConfig.Name = k3d.DefaultClusterName |
||||
} |
||||
|
||||
// fetch latest image
|
||||
if simpleConfig.Image == "latest" { |
||||
simpleConfig.Image = version.GetK3sVersion(true) |
||||
} |
||||
|
||||
clusterNetwork := k3d.ClusterNetwork{} |
||||
if simpleConfig.Network != "" { |
||||
clusterNetwork.Name = simpleConfig.Network |
||||
clusterNetwork.External = true |
||||
} |
||||
|
||||
// -> API
|
||||
if simpleConfig.ExposeAPI.Host == "" { |
||||
simpleConfig.ExposeAPI.Host = k3d.DefaultAPIHost |
||||
} |
||||
if simpleConfig.ExposeAPI.HostIP == "" { |
||||
simpleConfig.ExposeAPI.HostIP = k3d.DefaultAPIHost |
||||
} |
||||
|
||||
kubeAPIExposureOpts := &k3d.ExposureOpts{ |
||||
Host: simpleConfig.ExposeAPI.Host, |
||||
} |
||||
kubeAPIExposureOpts.Port = k3d.DefaultAPIPort |
||||
kubeAPIExposureOpts.Binding = nat.PortBinding{ |
||||
HostIP: simpleConfig.ExposeAPI.HostIP, |
||||
HostPort: simpleConfig.ExposeAPI.HostPort, |
||||
} |
||||
|
||||
// FILL CLUSTER CONFIG
|
||||
newCluster := k3d.Cluster{ |
||||
Name: simpleConfig.Name, |
||||
Network: clusterNetwork, |
||||
Token: simpleConfig.ClusterToken, |
||||
KubeAPI: kubeAPIExposureOpts, |
||||
} |
||||
|
||||
// -> NODES
|
||||
newCluster.Nodes = []*k3d.Node{} |
||||
|
||||
if !simpleConfig.Options.K3dOptions.DisableLoadbalancer { |
||||
newCluster.ServerLoadBalancer = &k3d.Node{ |
||||
Role: k3d.LoadBalancerRole, |
||||
} |
||||
} |
||||
|
||||
/************* |
||||
* Add Nodes * |
||||
*************/ |
||||
|
||||
for i := 0; i < simpleConfig.Servers; i++ { |
||||
serverNode := k3d.Node{ |
||||
Role: k3d.ServerRole, |
||||
Image: simpleConfig.Image, |
||||
Args: simpleConfig.Options.K3sOptions.ExtraServerArgs, |
||||
ServerOpts: k3d.ServerOpts{}, |
||||
} |
||||
|
||||
// first server node will be init node if we have more than one server specified but no external datastore
|
||||
if i == 0 && simpleConfig.Servers > 1 { |
||||
serverNode.ServerOpts.IsInit = true |
||||
newCluster.InitNode = &serverNode |
||||
} |
||||
|
||||
newCluster.Nodes = append(newCluster.Nodes, &serverNode) |
||||
} |
||||
|
||||
for i := 0; i < simpleConfig.Agents; i++ { |
||||
agentNode := k3d.Node{ |
||||
Role: k3d.AgentRole, |
||||
Image: simpleConfig.Image, |
||||
Args: simpleConfig.Options.K3sOptions.ExtraAgentArgs, |
||||
} |
||||
newCluster.Nodes = append(newCluster.Nodes, &agentNode) |
||||
} |
||||
|
||||
/**************************** |
||||
* Extra Node Configuration * |
||||
****************************/ |
||||
|
||||
// -> VOLUMES
|
||||
nodeCount := simpleConfig.Servers + simpleConfig.Agents |
||||
nodeList := newCluster.Nodes |
||||
if !simpleConfig.Options.K3dOptions.DisableLoadbalancer { |
||||
nodeCount++ |
||||
nodeList = append(nodeList, newCluster.ServerLoadBalancer) |
||||
} |
||||
for _, volumeWithNodeFilters := range simpleConfig.Volumes { |
||||
nodes, err := util.FilterNodes(newCluster.Nodes, volumeWithNodeFilters.NodeFilters) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
for _, node := range nodes { |
||||
node.Volumes = append(node.Volumes, volumeWithNodeFilters.Volume) |
||||
} |
||||
} |
||||
|
||||
// -> PORTS
|
||||
for _, portWithNodeFilters := range simpleConfig.Ports { |
||||
if len(portWithNodeFilters.NodeFilters) == 0 && nodeCount > 1 { |
||||
return nil, fmt.Errorf("Portmapping '%s' lacks a node filter, but there's more than one node", portWithNodeFilters.Port) |
||||
} |
||||
|
||||
nodes, err := util.FilterNodes(nodeList, portWithNodeFilters.NodeFilters) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
for _, node := range nodes { |
||||
portmappings, err := nat.ParsePortSpec(portWithNodeFilters.Port) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("Failed to parse port spec '%s': %+v", portWithNodeFilters.Port, err) |
||||
} |
||||
if node.Ports == nil { |
||||
node.Ports = nat.PortMap{} |
||||
} |
||||
for _, pm := range portmappings { |
||||
if _, exists := node.Ports[pm.Port]; exists { |
||||
node.Ports[pm.Port] = append(node.Ports[pm.Port], pm.Binding) |
||||
} else { |
||||
node.Ports[pm.Port] = []nat.PortBinding{pm.Binding} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// -> LABELS
|
||||
for _, labelWithNodeFilters := range simpleConfig.Labels { |
||||
if len(labelWithNodeFilters.NodeFilters) == 0 && nodeCount > 1 { |
||||
return nil, fmt.Errorf("Labelmapping '%s' lacks a node filter, but there's more than one node", labelWithNodeFilters.Label) |
||||
} |
||||
|
||||
nodes, err := util.FilterNodes(nodeList, labelWithNodeFilters.NodeFilters) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
for _, node := range nodes { |
||||
if node.Labels == nil { |
||||
node.Labels = make(map[string]string) // ensure that the map is initialized
|
||||
} |
||||
k, v := util.SplitLabelKeyValue(labelWithNodeFilters.Label) |
||||
node.Labels[k] = v |
||||
} |
||||
} |
||||
|
||||
// -> ENV
|
||||
for _, envVarWithNodeFilters := range simpleConfig.Env { |
||||
if len(envVarWithNodeFilters.NodeFilters) == 0 && nodeCount > 1 { |
||||
return nil, fmt.Errorf("EnvVarMapping '%s' lacks a node filter, but there's more than one node", envVarWithNodeFilters.EnvVar) |
||||
} |
||||
|
||||
nodes, err := util.FilterNodes(nodeList, envVarWithNodeFilters.NodeFilters) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
for _, node := range nodes { |
||||
node.Env = append(node.Env, envVarWithNodeFilters.EnvVar) |
||||
} |
||||
} |
||||
|
||||
/************************** |
||||
* Cluster Create Options * |
||||
**************************/ |
||||
|
||||
clusterCreateOpts := k3d.ClusterCreateOpts{ |
||||
DisableImageVolume: simpleConfig.Options.K3dOptions.DisableImageVolume, |
||||
WaitForServer: simpleConfig.Options.K3dOptions.Wait, |
||||
Timeout: simpleConfig.Options.K3dOptions.Timeout, |
||||
DisableLoadBalancer: simpleConfig.Options.K3dOptions.DisableLoadbalancer, |
||||
K3sServerArgs: simpleConfig.Options.K3sOptions.ExtraServerArgs, |
||||
K3sAgentArgs: simpleConfig.Options.K3sOptions.ExtraAgentArgs, |
||||
GlobalLabels: map[string]string{}, // empty init
|
||||
GlobalEnv: []string{}, // empty init
|
||||
} |
||||
|
||||
// ensure, that we have the default object labels
|
||||
for k, v := range k3d.DefaultObjectLabels { |
||||
clusterCreateOpts.GlobalLabels[k] = v |
||||
} |
||||
|
||||
/* |
||||
* Registries |
||||
*/ |
||||
if simpleConfig.Registries.Create { |
||||
regPort, err := cliutil.ParsePortExposureSpec("random", k3d.DefaultRegistryPort) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("Failed to get port for registry: %+v", err) |
||||
} |
||||
clusterCreateOpts.Registries.Create = &k3d.Registry{ |
||||
Host: fmt.Sprintf("%s-%s-registry", k3d.DefaultObjectNamePrefix, newCluster.Name), |
||||
Image: fmt.Sprintf("%s:%s", k3d.DefaultRegistryImageRepo, k3d.DefaultRegistryImageTag), |
||||
ExposureOpts: *regPort, |
||||
} |
||||
} |
||||
|
||||
for _, usereg := range simpleConfig.Registries.Use { |
||||
reg, err := util.ParseRegistryRef(usereg) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("Failed to parse use-registry string '%s': %+v", usereg, err) |
||||
} |
||||
log.Tracef("Parsed registry reference: %+v", reg) |
||||
clusterCreateOpts.Registries.Use = append(clusterCreateOpts.Registries.Use, reg) |
||||
} |
||||
|
||||
/********************** |
||||
* Kubeconfig Options * |
||||
**********************/ |
||||
|
||||
// Currently, the kubeconfig options for the cluster config are the same as for the simple config
|
||||
|
||||
/****************************** |
||||
* Create Full Cluster Config * |
||||
******************************/ |
||||
|
||||
clusterConfig := &conf.ClusterConfig{ |
||||
Cluster: newCluster, |
||||
ClusterCreateOpts: clusterCreateOpts, |
||||
KubeconfigOpts: simpleConfig.Options.KubeconfigOptions, |
||||
} |
||||
|
||||
return clusterConfig, nil |
||||
} |
@ -0,0 +1,55 @@ |
||||
/* |
||||
Copyright © 2020 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 config |
||||
|
||||
import ( |
||||
"context" |
||||
"testing" |
||||
|
||||
conf "github.com/rancher/k3d/v4/pkg/config/v1alpha1" |
||||
"github.com/rancher/k3d/v4/pkg/runtimes" |
||||
) |
||||
|
||||
func TestTransformSimpleConfigToClusterConfig(t *testing.T) { |
||||
cfgFile := "./test_assets/config_test_simple.yaml" |
||||
|
||||
cfg, err := ReadConfig(cfgFile) |
||||
if err != nil { |
||||
t.Error(err) |
||||
} |
||||
|
||||
simpleCfg, ok := cfg.(conf.SimpleConfig) |
||||
if !ok { |
||||
t.Error("Config is not of type SimpleConfig") |
||||
} |
||||
|
||||
t.Logf("\n========== Read Config ==========\n%+v\n=================================\n", simpleCfg) |
||||
|
||||
clusterCfg, err := TransformSimpleToClusterConfig(context.Background(), runtimes.Docker, simpleCfg) |
||||
if err != nil { |
||||
t.Error(err) |
||||
} |
||||
|
||||
t.Logf("\n===== Resulting Cluster Config =====\n%+v\n===============\n", clusterCfg) |
||||
|
||||
} |
@ -0,0 +1,167 @@ |
||||
/* |
||||
Copyright © 2020 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 v1alpha1 |
||||
|
||||
import ( |
||||
"fmt" |
||||
"time" |
||||
|
||||
k3d "github.com/rancher/k3d/v4/pkg/types" |
||||
"github.com/rancher/k3d/v4/version" |
||||
) |
||||
|
||||
// DefaultConfigTpl for printing
|
||||
const DefaultConfigTpl = `--- |
||||
apiVersion: k3d.io/v1alpha1 |
||||
kind: Simple |
||||
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.GetK3sVersion(false)), |
||||
) |
||||
|
||||
// TypeMeta, basically copied from https://github.com/kubernetes/apimachinery/blob/a3b564b22db316a41e94fdcffcf9995424fe924c/pkg/apis/meta/v1/types.go#L36-L56
|
||||
type TypeMeta struct { |
||||
Kind string `mapstructure:"kind,omitempty" yaml:"kind,omitempty" json:"kind,omitempty"` |
||||
APIVersion string `mapstructure:"apiVersion,omitempty" yaml:"apiVersion,omitempty" json:"apiVersion,omitempty"` |
||||
} |
||||
|
||||
// Config interface.
|
||||
type Config interface { |
||||
GetKind() string |
||||
} |
||||
|
||||
type VolumeWithNodeFilters struct { |
||||
Volume string `mapstructure:"volume" yaml:"volume" json:"volume,omitempty"` |
||||
NodeFilters []string `mapstructure:"nodeFilters" yaml:"nodeFilters" json:"nodeFilters,omitempty"` |
||||
} |
||||
|
||||
type PortWithNodeFilters struct { |
||||
Port string `mapstructure:"port" yaml:"port" json:"port,omitempty"` |
||||
NodeFilters []string `mapstructure:"nodeFilters" yaml:"nodeFilters" json:"nodeFilters,omitempty"` |
||||
} |
||||
|
||||
type LabelWithNodeFilters struct { |
||||
Label string `mapstructure:"label" yaml:"label" json:"label,omitempty"` |
||||
NodeFilters []string `mapstructure:"nodeFilters" yaml:"nodeFilters" json:"nodeFilters,omitempty"` |
||||
} |
||||
|
||||
type EnvVarWithNodeFilters struct { |
||||
EnvVar string `mapstructure:"envVar" yaml:"envVar" json:"envVar,omitempty"` |
||||
NodeFilters []string `mapstructure:"nodeFilters" yaml:"nodeFilters" json:"nodeFilters,omitempty"` |
||||
} |
||||
|
||||
// SimpleConfigOptionsKubeconfig describes the set of options referring to the kubeconfig during cluster creation.
|
||||
type SimpleConfigOptionsKubeconfig struct { |
||||
UpdateDefaultKubeconfig bool `mapstructure:"updateDefaultKubeconfig" yaml:"updateDefaultKubeconfig" json:"updateDefaultKubeconfig,omitempty"` // default: true
|
||||
SwitchCurrentContext bool `mapstructure:"switchCurrentContext" yaml:"switchCurrentContext" json:"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"` |
||||
} |
||||
|
||||
type SimpleConfigOptionsK3d struct { |
||||
Wait bool `mapstructure:"wait" yaml:"wait"` |
||||
Timeout time.Duration `mapstructure:"timeout" yaml:"timeout"` |
||||
DisableLoadbalancer bool `mapstructure:"disableLoadbalancer" yaml:"disableLoadbalancer"` |
||||
DisableImageVolume bool `mapstructure:"disableImageVolume" yaml:"disableImageVolume"` |
||||
NoRollback bool `mapstructure:"noRollback" yaml:"noRollback"` |
||||
PrepDisableHostIPInjection bool `mapstructure:"prepDisableHostIPInjection" yaml:"prepDisableHostIPInjection"` |
||||
NodeHookActions []k3d.NodeHookAction `mapstructure:"nodeHookActions" yaml:"nodeHookActions,omitempty"` |
||||
} |
||||
|
||||
type SimpleConfigOptionsK3s struct { |
||||
ExtraServerArgs []string `mapstructure:"extraServerArgs" yaml:"extraServerArgs"` |
||||
ExtraAgentArgs []string `mapstructure:"extraAgentArgs" yaml:"extraAgentArgs"` |
||||
} |
||||
|
||||
// SimpleConfig describes the toplevel k3d configuration file.
|
||||
type SimpleConfig struct { |
||||
TypeMeta `mapstructure:",squash" yaml:",inline"` |
||||
Name string `mapstructure:"name" yaml:"name" json:"name,omitempty"` |
||||
Servers int `mapstructure:"servers" yaml:"servers" json:"servers,omitempty"` //nolint:lll // default 1
|
||||
Agents int `mapstructure:"agents" yaml:"agents" json:"agents,omitempty"` //nolint:lll // default 0
|
||||
ExposeAPI SimpleExposureOpts `mapstructure:"kubeAPI" yaml:"kubeAPI" json:"kubeAPI,omitempty"` |
||||
Image string `mapstructure:"image" yaml:"image" json:"image,omitempty"` |
||||
Network string `mapstructure:"network" yaml:"network" json:"network,omitempty"` |
||||
ClusterToken string `mapstructure:"clusterToken" yaml:"clusterToken" json:"clusterToken,omitempty"` // default: auto-generated
|
||||
Volumes []VolumeWithNodeFilters `mapstructure:"volumes" yaml:"volumes" json:"volumes,omitempty"` |
||||
Ports []PortWithNodeFilters `mapstructure:"ports" yaml:"ports" json:"ports,omitempty"` |
||||
Labels []LabelWithNodeFilters `mapstructure:"labels" yaml:"labels" json:"labels,omitempty"` |
||||
Options SimpleConfigOptions `mapstructure:"options" yaml:"options" json:"options,omitempty"` |
||||
Env []EnvVarWithNodeFilters `mapstructure:"env" yaml:"env" json:"env,omitempty"` |
||||
Registries struct { |
||||
Use []string `mapstructure:"use" yaml:"use,omitempty" json:"use,omitempty"` |
||||
Create bool `mapstructure:"create" yaml:"create,omitempty" json:"create,omitempty"` |
||||
} `mapstructure:"registries" yaml:"registries,omitempty" json:"registries,omitempty"` |
||||
} |
||||
|
||||
// SimpleExposureOpts provides a simplified syntax compared to the original k3d.ExposureOpts
|
||||
type SimpleExposureOpts struct { |
||||
Host string `mapstructure:"host" yaml:"host,omitempty" json:"host,omitempty"` |
||||
HostIP string `mapstructure:"hostIP" yaml:"hostIP,omitempty" json:"hostIP,omitempty"` |
||||
HostPort string `mapstructure:"hostPort" yaml:"hostPort,omitempty" json:"hostPort,omitempty"` |
||||
} |
||||
|
||||
// GetKind implements Config.GetKind
|
||||
func (c SimpleConfig) GetKind() string { |
||||
return "Cluster" |
||||
} |
||||
|
||||
// ClusterConfig describes a single cluster config
|
||||
type ClusterConfig struct { |
||||
TypeMeta `mapstructure:",squash" yaml:",inline"` |
||||
Cluster k3d.Cluster `mapstructure:",squash" yaml:",inline"` |
||||
ClusterCreateOpts k3d.ClusterCreateOpts `mapstructure:"options" yaml:"options"` |
||||
KubeconfigOpts SimpleConfigOptionsKubeconfig `mapstructure:"kubeconfig" yaml:"kubeconfig"` |
||||
} |
||||
|
||||
// GetKind implements Config.GetKind
|
||||
func (c ClusterConfig) GetKind() string { |
||||
return "Cluster" |
||||
} |
||||
|
||||
// ClusterListConfig describes a list of clusters
|
||||
type ClusterListConfig struct { |
||||
TypeMeta `mapstructure:",squash" yaml:",inline"` |
||||
Clusters []k3d.Cluster `mapstructure:"clusters" yaml:"clusters"` |
||||
} |
||||
|
||||
func (c ClusterListConfig) GetKind() string { |
||||
return "ClusterList" |
||||
} |
@ -0,0 +1,84 @@ |
||||
/* |
||||
Copyright © 2020 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 config |
||||
|
||||
import ( |
||||
"context" |
||||
"time" |
||||
|
||||
k3dc "github.com/rancher/k3d/v4/pkg/client" |
||||
conf "github.com/rancher/k3d/v4/pkg/config/v1alpha1" |
||||
"github.com/rancher/k3d/v4/pkg/runtimes" |
||||
k3d "github.com/rancher/k3d/v4/pkg/types" |
||||
"github.com/rancher/k3d/v4/pkg/util" |
||||
|
||||
"fmt" |
||||
|
||||
log "github.com/sirupsen/logrus" |
||||
) |
||||
|
||||
// ValidateClusterConfig checks a given cluster config for basic errors
|
||||
func ValidateClusterConfig(ctx context.Context, runtime runtimes.Runtime, config conf.ClusterConfig) error { |
||||
// cluster name must be a valid host name
|
||||
if err := k3dc.CheckName(config.Cluster.Name); err != nil { |
||||
log.Errorf("Provided cluster name '%s' does not match requirements", config.Cluster.Name) |
||||
|
||||
return err |
||||
} |
||||
|
||||
// network:: edge case: hostnetwork -> only if we have a single node (to avoid port collisions)
|
||||
if config.Cluster.Network.Name == "host" && len(config.Cluster.Nodes) > 1 { |
||||
return fmt.Errorf("Can only use hostnetwork mode with a single node (port collisions, etc.)") |
||||
} |
||||
|
||||
// timeout can't be negative
|
||||
if config.ClusterCreateOpts.Timeout < 0*time.Second { |
||||
return fmt.Errorf("Timeout may not be negative (is '%s')", config.ClusterCreateOpts.Timeout) |
||||
} |
||||
|
||||
// API-Port cannot be changed when using network=host
|
||||
if config.Cluster.Network.Name == "host" && config.Cluster.KubeAPI.Port.Port() != k3d.DefaultAPIPort { |
||||
// in hostNetwork mode, we're not going to map a hostport. Here it should always use 6443.
|
||||
// Note that hostNetwork mode is super inflexible and since we don't change the backend port (on the container), it will only be one hostmode cluster allowed.
|
||||
return fmt.Errorf("The API Port can not be changed when using 'host' network") |
||||
} |
||||
|
||||
// validate nodes one by one
|
||||
for _, node := range config.Cluster.Nodes { |
||||
|
||||
// node names have to be valid hostnames // TODO: validate hostnames once we generate them before this step
|
||||
/*if err := k3dc.CheckName(node.Name); err != nil { |
||||
return err |
||||
}*/ |
||||
|
||||
// volumes have to be either an existing path on the host or a named runtime volume
|
||||
for _, volume := range node.Volumes { |
||||
|
||||
if err := util.ValidateVolumeMount(runtime, volume); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
@ -0,0 +1,44 @@ |
||||
/* |
||||
Copyright © 2020 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 config |
||||
|
||||
import ( |
||||
"context" |
||||
"testing" |
||||
|
||||
conf "github.com/rancher/k3d/v4/pkg/config/v1alpha1" |
||||
"github.com/rancher/k3d/v4/pkg/runtimes" |
||||
) |
||||
|
||||
func TestValidateClusterConfig(t *testing.T) { |
||||
cfgFile := "./test_assets/config_test_cluster.yaml" |
||||
|
||||
cfg, err := ReadConfig(cfgFile) |
||||
if err != nil { |
||||
t.Error(err) |
||||
} |
||||
|
||||
if err := ValidateClusterConfig(context.Background(), runtimes.Docker, cfg.(conf.ClusterConfig)); err != nil { |
||||
t.Error(err) |
||||
} |
||||
} |
@ -0,0 +1,30 @@ |
||||
/* |
||||
Copyright © 2020 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 runtimes |
||||
|
||||
import "errors" |
||||
|
||||
// ErrRuntimeNetworkNotEmpty describes an error that occurs because a network still has containers connected to it (e.g. cannot be deleted)
|
||||
var ErrRuntimeNetworkNotEmpty = errors.New("network not empty") |
||||
|
||||
// ErrRuntimeContainerUnknown describes the situation, where we're inspecting a container that's not obviously managed by k3d
|
||||
var ErrRuntimeContainerUnknown = errors.New("container not managed by k3d: missing default label(s)") |
@ -0,0 +1,81 @@ |
||||
/* |
||||
Copyright © 2020 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 k3s |
||||
|
||||
/* |
||||
* Copied from https://github.com/k3s-io/k3s/blob/cf8c101b705c7af20e2ed11df43beb4951e6d9dc/pkg/agent/templates/registry.go
|
||||
* .. to avoid pulling in k3s as a dependency |
||||
*/ |
||||
|
||||
// Mirror contains the config related to the registry mirror
|
||||
type Mirror struct { |
||||
// Endpoints are endpoints for a namespace. CRI plugin will try the endpoints
|
||||
// one by one until a working one is found. The endpoint must be a valid url
|
||||
// with host specified.
|
||||
// The scheme, host and path from the endpoint URL will be used.
|
||||
Endpoints []string `toml:"endpoint" yaml:"endpoint"` |
||||
} |
||||
|
||||
// AuthConfig contains the config related to authentication to a specific registry
|
||||
type AuthConfig struct { |
||||
// Username is the username to login the registry.
|
||||
Username string `toml:"username" yaml:"username"` |
||||
// Password is the password to login the registry.
|
||||
Password string `toml:"password" yaml:"password"` |
||||
// Auth is a base64 encoded string from the concatenation of the username,
|
||||
// a colon, and the password.
|
||||
Auth string `toml:"auth" yaml:"auth"` |
||||
// IdentityToken is used to authenticate the user and get
|
||||
// an access token for the registry.
|
||||
IdentityToken string `toml:"identitytoken" yaml:"identity_token"` |
||||
} |
||||
|
||||
// TLSConfig contains the CA/Cert/Key used for a registry
|
||||
type TLSConfig struct { |
||||
CAFile string `toml:"ca_file" yaml:"ca_file"` |
||||
CertFile string `toml:"cert_file" yaml:"cert_file"` |
||||
KeyFile string `toml:"key_file" yaml:"key_file"` |
||||
InsecureSkipVerify bool `toml:"insecure_skip_verify" yaml:"insecure_skip_verify"` |
||||
} |
||||
|
||||
// Registry is registry settings configured
|
||||
type Registry struct { |
||||
// Mirrors are namespace to mirror mapping for all namespaces.
|
||||
Mirrors map[string]Mirror `toml:"mirrors" yaml:"mirrors"` |
||||
// Configs are configs for each registry.
|
||||
// The key is the FDQN or IP of the registry.
|
||||
Configs map[string]RegistryConfig `toml:"configs" yaml:"configs"` |
||||
|
||||
// Auths are registry endpoint to auth config mapping. The registry endpoint must
|
||||
// be a valid url with host specified.
|
||||
// DEPRECATED: Use Configs instead. Remove in containerd 1.4.
|
||||
Auths map[string]AuthConfig `toml:"auths" yaml:"auths"` |
||||
} |
||||
|
||||
// RegistryConfig contains configuration used to communicate with the registry.
|
||||
type RegistryConfig struct { |
||||
// Auth contains information to authenticate to the registry.
|
||||
Auth *AuthConfig `toml:"auth" yaml:"auth"` |
||||
// TLS is a pair of CA/Cert/Key which then are used when creating the transport
|
||||
// that communicates with the registry.
|
||||
TLS *TLSConfig `toml:"tls" yaml:"tls"` |
||||
} |
@ -0,0 +1,94 @@ |
||||
/* |
||||
Copyright © 2020 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 k8s |
||||
|
||||
/* |
||||
* Source: https://github.com/kubernetes/enhancements/blob/0d69f7cea6fbe73a7d70fab569c6898f5ccb7be0/keps/sig-cluster-lifecycle/generic/1755-communicating-a-local-registry/README.md#specification-for-localregistryhosting-v1
|
||||
* Copied over: 07.01.2020 |
||||
* Original License |
||||
* > Copyright 2020 The Kubernetes Authors |
||||
* > |
||||
* > 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. |
||||
*/ |
||||
|
||||
// LocalRegistryHostingV1 describes a local registry that developer tools can
|
||||
// connect to. A local registry allows clients to load images into the local
|
||||
// cluster by pushing to this registry.
|
||||
type LocalRegistryHostingV1 struct { |
||||
// Host documents the host (hostname and port) of the registry, as seen from
|
||||
// outside the cluster.
|
||||
//
|
||||
// This is the registry host that tools outside the cluster should push images
|
||||
// to.
|
||||
Host string `yaml:"host,omitempty" json:"host,omitempty"` |
||||
|
||||
// HostFromClusterNetwork documents the host (hostname and port) of the
|
||||
// registry, as seen from networking inside the container pods.
|
||||
//
|
||||
// This is the registry host that tools running on pods inside the cluster
|
||||
// should push images to. If not set, then tools inside the cluster should
|
||||
// assume the local registry is not available to them.
|
||||
HostFromClusterNetwork string `yaml:"hostFromClusterNetwork,omitempty" json:"hostFromClusterNetwork,omitempty"` |
||||
|
||||
// HostFromContainerRuntime documents the host (hostname and port) of the
|
||||
// registry, as seen from the cluster's container runtime.
|
||||
//
|
||||
// When tools apply Kubernetes objects to the cluster, this host should be
|
||||
// used for image name fields. If not set, users of this field should use the
|
||||
// value of Host instead.
|
||||
//
|
||||
// Note that it doesn't make sense semantically to define this field, but not
|
||||
// define Host or HostFromClusterNetwork. That would imply a way to pull
|
||||
// images without a way to push images.
|
||||
HostFromContainerRuntime string `yaml:"hostFromContainerRuntime,omitempty" json:"hostFromContainerRuntime,omitempty"` |
||||
|
||||
// Help contains a URL pointing to documentation for users on how to set
|
||||
// up and configure a local registry.
|
||||
//
|
||||
// Tools can use this to nudge users to enable the registry. When possible,
|
||||
// the writer should use as permanent a URL as possible to prevent drift
|
||||
// (e.g., a version control SHA).
|
||||
//
|
||||
// When image pushes to a registry host specified in one of the other fields
|
||||
// fail, the tool should display this help URL to the user. The help URL
|
||||
// should contain instructions on how to diagnose broken or misconfigured
|
||||
// registries.
|
||||
Help string `yaml:"help,omitempty" json:"help,omitempty"` |
||||
} |
||||
|
||||
// LocalRegistryHosting defaults
|
||||
const ( |
||||
LocalRegistryHostingNamespace = "kube-public" |
||||
LocalRegistryHostingName = "local-registry-hosting" |
||||
LocalRegistryHostingData = "localRegistryHosting.v1" |
||||
) |
@ -0,0 +1,190 @@ |
||||
/* |
||||
Copyright © 2020 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 ( |
||||
"fmt" |
||||
"regexp" |
||||
"strconv" |
||||
"strings" |
||||
|
||||
k3d "github.com/rancher/k3d/v4/pkg/types" |
||||
log "github.com/sirupsen/logrus" |
||||
) |
||||
|
||||
// Regexp pattern to match node filters
|
||||
var filterRegexp = regexp.MustCompile(`^(?P<group>server|agent|loadbalancer|all)(?P<subsetSpec>\[(?P<subset>(?P<subsetList>(\d+,?)+)|(?P<subsetRange>\d*:\d*)|(?P<subsetWildcard>\*))\])?$`) |
||||
|
||||
// FilterNodes takes a string filter to return a filtered list of nodes
|
||||
func FilterNodes(nodes []*k3d.Node, filters []string) ([]*k3d.Node, error) { |
||||
|
||||
if len(filters) == 0 || len(filters[0]) == 0 { |
||||
log.Warnln("No node filter specified") |
||||
return nodes, nil |
||||
} |
||||
|
||||
// map roles to subsets
|
||||
serverNodes := []*k3d.Node{} |
||||
agentNodes := []*k3d.Node{} |
||||
var serverlb *k3d.Node |
||||
for _, node := range nodes { |
||||
if node.Role == k3d.ServerRole { |
||||
serverNodes = append(serverNodes, node) |
||||
} else if node.Role == k3d.AgentRole { |
||||
agentNodes = append(agentNodes, node) |
||||
} else if node.Role == k3d.LoadBalancerRole { |
||||
serverlb = node |
||||
} |
||||
} |
||||
|
||||
filteredNodes := []*k3d.Node{} |
||||
set := make(map[*k3d.Node]struct{}) |
||||
|
||||
// range over all instances of group[subset] specs
|
||||
for _, filter := range filters { |
||||
|
||||
// match regex with capturing groups
|
||||
match := filterRegexp.FindStringSubmatch(filter) |
||||
|
||||
if len(match) == 0 { |
||||
return nil, fmt.Errorf("Failed to parse node filters: invalid format or empty subset in '%s'", filter) |
||||
} |
||||
|
||||
// map capturing group names to submatches
|
||||
submatches := MapSubexpNames(filterRegexp.SubexpNames(), match) |
||||
|
||||
// if one of the filters is 'all', we only return this and drop all others
|
||||
if submatches["group"] == "all" { |
||||
// TODO: filterNodes: only log if really more than one is specified
|
||||
log.Warnf("Node filter 'all' set, but more were specified in '%+v'", filters) |
||||
return nodes, nil |
||||
} |
||||
|
||||
// Choose the group of nodes to operate on
|
||||
groupNodes := []*k3d.Node{} |
||||
if submatches["group"] == string(k3d.ServerRole) { |
||||
groupNodes = serverNodes |
||||
} else if submatches["group"] == string(k3d.AgentRole) { |
||||
groupNodes = agentNodes |
||||
} else if submatches["group"] == string(k3d.LoadBalancerRole) { |
||||
if serverlb == nil { |
||||
return nil, fmt.Errorf("Node filter '%s' targets a node that does not exist (disabled?)", filter) |
||||
} |
||||
filteredNodes = append(filteredNodes, serverlb) |
||||
return filteredNodes, nil // early exit if filtered group is the loadbalancer
|
||||
} |
||||
|
||||
/* Option 1) subset defined by list */ |
||||
if submatches["subsetList"] != "" { |
||||
for _, index := range strings.Split(submatches["subsetList"], ",") { |
||||
if index != "" { |
||||
num, err := strconv.Atoi(index) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("Failed to convert subset number to integer in '%s'", filter) |
||||
} |
||||
if num < 0 || num >= len(groupNodes) { |
||||
return nil, fmt.Errorf("Index out of range: index '%d' < 0 or > number of available nodes in filter '%s'", num, filter) |
||||
} |
||||
if _, exists := set[groupNodes[num]]; !exists { |
||||
filteredNodes = append(filteredNodes, groupNodes[num]) |
||||
set[groupNodes[num]] = struct{}{} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/* Option 2) subset defined by range */ |
||||
} else if submatches["subsetRange"] != "" { |
||||
|
||||
/* |
||||
* subset specified by a range 'START:END', where each side is optional |
||||
*/ |
||||
|
||||
split := strings.Split(submatches["subsetRange"], ":") |
||||
if len(split) != 2 { |
||||
return nil, fmt.Errorf("Failed to parse subset range in '%s'", filter) |
||||
} |
||||
|
||||
start := 0 |
||||
end := len(groupNodes) - 1 |
||||
|
||||
var err error |
||||
|
||||
if split[0] != "" { |
||||
start, err = strconv.Atoi(split[0]) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("Failed to convert subset range start to integer in '%s'", filter) |
||||
} |
||||
if start < 0 || start >= len(groupNodes) { |
||||
return nil, fmt.Errorf("Invalid subset range: start < 0 or > number of available nodes in '%s'", filter) |
||||
} |
||||
|
||||
} |
||||
|
||||
if split[1] != "" { |
||||
end, err = strconv.Atoi(split[1]) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("Failed to convert subset range start to integer in '%s'", filter) |
||||
} |
||||
if end < start || end >= len(groupNodes) { |
||||
return nil, fmt.Errorf("Invalid subset range: end < start or > number of available nodes in '%s'", filter) |
||||
} |
||||
} |
||||
|
||||
for i := start; i <= end; i++ { |
||||
if _, exists := set[groupNodes[i]]; !exists { |
||||
filteredNodes = append(filteredNodes, groupNodes[i]) |
||||
set[groupNodes[i]] = struct{}{} |
||||
} |
||||
} |
||||
|
||||
/* Option 3) subset defined by wildcard */ |
||||
} else if submatches["subsetWildcard"] == "*" { |
||||
/* |
||||
* '*' = all nodes |
||||
*/ |
||||
for _, node := range groupNodes { |
||||
if _, exists := set[node]; !exists { |
||||
filteredNodes = append(filteredNodes, node) |
||||
set[node] = struct{}{} |
||||
} |
||||
} |
||||
|
||||
/* Option X) invalid/unknown subset */ |
||||
} else { |
||||
return nil, fmt.Errorf("Failed to parse node specifiers: unknown subset in '%s'", filter) |
||||
} |
||||
|
||||
} |
||||
|
||||
return filteredNodes, nil |
||||
} |
||||
|
||||
// FilterNodesByRole returns a stripped list of nodes which do match the given role
|
||||
func FilterNodesByRole(nodes []*k3d.Node, role k3d.Role) []*k3d.Node { |
||||
filteredNodes := []*k3d.Node{} |
||||
for _, node := range nodes { |
||||
if node.Role == role { |
||||
filteredNodes = append(filteredNodes, node) |
||||
} |
||||
} |
||||
return filteredNodes |
||||
} |
@ -0,0 +1,39 @@ |
||||
/* |
||||
Copyright © 2020 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 ( |
||||
"strings" |
||||
) |
||||
|
||||
// SplitLabelKeyValue separates the label key from the label value (if any)
|
||||
func SplitLabelKeyValue(label string) (string, string) { |
||||
// split only on first '=' sign (like `docker run` do)
|
||||
labelSlice := strings.SplitN(label, "=", 2) |
||||
|
||||
if len(labelSlice) > 1 { |
||||
return labelSlice[0], labelSlice[1] |
||||
} |
||||
|
||||
// defaults to label key with empty value (like `docker run` do)
|
||||
return label, "" |
||||
} |
@ -0,0 +1,47 @@ |
||||
/* |
||||
Copyright © 2020 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 ( |
||||
"net" |
||||
|
||||
log "github.com/sirupsen/logrus" |
||||
) |
||||
|
||||
// GetFreePort tries to fetch an open port from the OS-Kernel
|
||||
func GetFreePort() (int, error) { |
||||
tcpAddress, err := net.ResolveTCPAddr("tcp", "localhost:0") |
||||
if err != nil { |
||||
log.Errorln("Failed to resolve address") |
||||
return 0, err |
||||
} |
||||
|
||||
tcpListener, err := net.ListenTCP("tcp", tcpAddress) |
||||
if err != nil { |
||||
log.Errorln("Failed to create TCP Listener") |
||||
return 0, err |
||||
} |
||||
defer tcpListener.Close() |
||||
|
||||
return tcpListener.Addr().(*net.TCPAddr).Port, nil |
||||
} |
@ -0,0 +1,60 @@ |
||||
/* |
||||
Copyright © 2020 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 ( |
||||
"fmt" |
||||
"regexp" |
||||
|
||||
"github.com/docker/go-connections/nat" |
||||
k3d "github.com/rancher/k3d/v4/pkg/types" |
||||
) |
||||
|
||||
var registryRefRegexp = regexp.MustCompile(`^(?P<protocol>http:\/\/|https:\/\/)?(?P<hostref>(?P<hostip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?P<hostname>[a-zA-Z\-\.0-9]+)){1}?((:)(?P<internalport>\d{1,5}))?((:)(?P<externalport>\d{1,5}))?$`) |
||||
|
||||
// ParseRegistryRef returns a registry struct parsed from a simplified definition string
|
||||
func ParseRegistryRef(registryRef string) (*k3d.Registry, error) { |
||||
match := registryRefRegexp.FindStringSubmatch(registryRef) |
||||
|
||||
if len(match) == 0 { |
||||
return nil, fmt.Errorf("Failed to parse registry reference %s: Must be [proto://]host[:port]", registryRef) |
||||
} |
||||
|
||||
submatches := MapSubexpNames(registryRefRegexp.SubexpNames(), match) |
||||
|
||||
registry := &k3d.Registry{ |
||||
Host: submatches["hostref"], |
||||
Protocol: submatches["protocol"], |
||||
ExposureOpts: k3d.ExposureOpts{}, |
||||
} |
||||
registry.ExposureOpts.Host = submatches["hostref"] |
||||
|
||||
if submatches["port"] != "" { |
||||
registry.ExposureOpts.PortMapping = nat.PortMapping{ |
||||
Port: nat.Port(fmt.Sprintf("%s/tcp", submatches["internalport"])), |
||||
Binding: nat.PortBinding{ |
||||
HostPort: submatches["externalport"], |
||||
}, |
||||
} |
||||
} |
||||
return registry, nil |
||||
} |
@ -0,0 +1,92 @@ |
||||
/* |
||||
Copyright © 2020 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 ( |
||||
"fmt" |
||||
"os" |
||||
"strings" |
||||
|
||||
log "github.com/sirupsen/logrus" |
||||
|
||||
"github.com/rancher/k3d/v4/pkg/runtimes" |
||||
) |
||||
|
||||
// ValidateVolumeMount checks, if the source of volume mounts exists and if the destination is an absolute path
|
||||
// - SRC: source directory/file -> tests: must exist
|
||||
// - DEST: source directory/file -> tests: must be absolute path
|
||||
func ValidateVolumeMount(runtime runtimes.Runtime, volumeMount string) error { |
||||
src := "" |
||||
dest := "" |
||||
|
||||
// validate 'SRC[:DEST]' substring
|
||||
split := strings.Split(volumeMount, ":") |
||||
if len(split) < 1 { |
||||
return fmt.Errorf("No volume/path specified") |
||||
} |
||||
if len(split) > 3 { |
||||
return fmt.Errorf("Invalid volume mount '%s': maximal 2 ':' allowed", volumeMount) |
||||
} |
||||
|
||||
// we only have SRC specified -> DEST = SRC
|
||||
if len(split) == 1 { |
||||
src = split[0] |
||||
dest = src |
||||
} else { |
||||
src = split[0] |
||||
dest = split[1] |
||||
} |
||||
|
||||
// verify that the source exists
|
||||
if src != "" { |
||||
// a) named volume
|
||||
isNamedVolume := true |
||||
if err := verifyNamedVolume(runtime, src); err != nil { |
||||
isNamedVolume = false |
||||
} |
||||
if !isNamedVolume { |
||||
if _, err := os.Stat(src); err != nil { |
||||
log.Warnf("Failed to stat file/directory/named volume that you're trying to mount: '%s' in '%s' -> Please make sure it exists", src, volumeMount) |
||||
} |
||||
} |
||||
} |
||||
|
||||
// verify that the destination is an absolute path
|
||||
if !strings.HasPrefix(dest, "/") { |
||||
return fmt.Errorf("Volume mount destination doesn't appear to be an absolute path: '%s' in '%s'", dest, volumeMount) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// verifyNamedVolume checks whether a named volume exists in the runtime
|
||||
func verifyNamedVolume(runtime runtimes.Runtime, volumeName string) error { |
||||
volumeName, err := runtime.GetVolume(volumeName) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
if volumeName == "" { |
||||
return fmt.Errorf("Failed to find named volume '%s'", volumeName) |
||||
} |
||||
return nil |
||||
} |
@ -0,0 +1,46 @@ |
||||
apiVersion: k3d.io/v1alpha1 |
||||
kind: Simple |
||||
name: test |
||||
servers: 1 |
||||
agents: 2 |
||||
exposeAPI: |
||||
hostIP: "0.0.0.0" |
||||
port: "6443" |
||||
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 |
||||
nodeFilters: |
||||
- all |
||||
labels: |
||||
- label: foo=bar |
||||
nodeFilters: |
||||
- server[0] |
||||
- loadbalancer |
||||
registries: |
||||
create: true |
||||
use: [] |
||||
|
||||
options: |
||||
k3d: |
||||
wait: true |
||||
timeout: "60s" |
||||
disableLoadbalancer: false |
||||
disableImageVolume: false |
||||
k3s: |
||||
extraServerArgs: |
||||
- --tls-san=127.0.0.1 |
||||
extraAgentArgs: [] |
||||
kubeconfig: |
||||
updateDefaultKubeconfig: true |
||||
switchCurrentContext: true |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue