Skip to content

Commit

Permalink
Merge pull request rancher#13 from paynejacob/feature/k3d
Browse files Browse the repository at this point in the history
  • Loading branch information
paynejacob committed Nov 29, 2021
2 parents d00530e + 3a51cf8 commit 793755a
Show file tree
Hide file tree
Showing 8 changed files with 395 additions and 12 deletions.
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -62,7 +62,7 @@ require (
github.com/coreos/go-oidc/v3 v3.0.0
github.com/coreos/go-semver v0.3.0
github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a // indirect
github.com/creasty/defaults v1.5.2 // indirect
github.com/creasty/defaults v1.5.2
github.com/crewjam/saml v0.4.5
github.com/davecgh/go-spew v1.1.1
github.com/docker/distribution v2.7.1+incompatible
Expand Down
8 changes: 8 additions & 0 deletions tests/framework/clients/k3d/config.go
@@ -0,0 +1,8 @@
package k3d

const ConfigurationFileKey = "k3d"

type Config struct {
image string `yaml:"image" default:"rancher/k3s:v1.21.3-k3s1"`
createTimeout int `yaml:"createTimeout" default:"120s"`
}
160 changes: 160 additions & 0 deletions tests/framework/clients/k3d/k3d.go
@@ -0,0 +1,160 @@
package k3d

import (
"context"
"fmt"
"os/exec"

"github.com/pkg/errors"
"github.com/rancher/rancher/pkg/api/scheme"
v3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
apisV1 "github.com/rancher/rancher/pkg/apis/provisioning.cattle.io/v1"
"github.com/rancher/rancher/tests/framework/clients/rancher"
management "github.com/rancher/rancher/tests/framework/clients/rancher/generated/management/v3"
"github.com/rancher/rancher/tests/framework/extensions/clusters"
"github.com/rancher/rancher/tests/framework/pkg/config"
"github.com/rancher/rancher/tests/framework/pkg/session"
"github.com/rancher/rancher/tests/framework/pkg/wait"
"github.com/rancher/wrangler/pkg/randomtoken"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)

// CreateK3DCluster creates a minimal k3d cluster and returns a rest config for connecting to the newly created cluster.
// If a name is given a random one will be generated.
func CreateK3DCluster(ts *session.Session, name string) (*rest.Config, error) {
k3dConfig := new(Config)
config.LoadConfig(ConfigurationFileKey, k3dConfig)

name = defaultName(name)

ts.RegisterCleanupFunc(func() error {
return DeleteK3DCluster(name)
})

msg, err := exec.Command("k3d", "cluster", "create", name,
"--no-lb",
"--no-hostip",
"--no-image-volume",
"--kubeconfig-update-default=false",
"--kubeconfig-switch-context=false",
fmt.Sprintf("--timeout=%d", k3dConfig.createTimeout),
`--k3s-server-arg=--no-deploy=traefik`,
`--k3s-server-arg=--no-deploy=servicelb`,
`--k3s-server-arg=--no-deploy=metrics-serve`,
`--k3s-server-arg=--no-deploy=local-storage`).CombinedOutput()
if err != nil {
return nil, errors.Wrap(err, "CreateK3DCluster: "+string(msg))
}

configBytes, err := exec.Command("k3d", "kubeconfig", "get", name).Output()
if err != nil {
return nil, errors.Wrap(err, "CreateK3DCluster: failed to get kubeconfig for k3d cluster")
}

restConfig, err := clientcmd.RESTConfigFromKubeConfig(configBytes)
if err != nil {
return nil, errors.Wrap(err, "CreateK3DCluster: failed to parse kubeconfig for k3d cluster")
}

return restConfig, nil
}

// DeleteK3DCluster deletes the k3d cluster with the given name. An error is returned if the cluster does not exist.
func DeleteK3DCluster(name string) error {
return exec.Command("k3d", "cluster", "delete", name).Run()
}

// CreateAndImportK3DCluster creates a new k3d cluster and imports it into rancher.
func CreateAndImportK3DCluster(client *rancher.Client, name string) (*management.Cluster, error) {
var err error

name = defaultName(name)

// create the provisioning cluster
cluster := &apisV1.Cluster{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: "fleet-default",
},
}
_, err = client.Provisioning.Clusters("fleet-default").Create(context.TODO(), cluster, metav1.CreateOptions{})
if err != nil {
return nil, errors.Wrap(err, "CreateAndImportK3DCluster: failed to create provisioning cluster")
}

// create the k3s cluster
downRest, err := CreateK3DCluster(client.Session, name)
if err != nil {
return nil, errors.Wrap(err, "CreateAndImportK3DCluster: failed to create k3d cluster")
}

// wait for the management cluster
mClusterWatch, err := client.GetManagementWatchInterface(management.ClusterType, metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "CreateAndImportK3DCluster: failed to watch for management cluster")
}

var mgmtCluster *management.Cluster
err = wait.WatchWait(mClusterWatch, func(event watch.Event) (bool, error) {
var mc v3.Cluster
err = runtime.DefaultUnstructuredConverter.FromUnstructured(event.Object.(*unstructured.Unstructured).Object, &mc)
if mc.Spec.DisplayName == name {
mgmtCluster, err = client.Management.Cluster.ByID(mc.Name)
return true, err
}

return false, nil

})
if err != nil {
return nil, errors.Wrap(err, "CreateAndImportK3DCluster: failed to watch for management cluster")
}

// import the k3d cluster
err = clusters.ImportCluster(client, mgmtCluster, downRest)
if err != nil {
return nil, errors.Wrap(err, "CreateAndImportK3DCluster: failed to import cluster")
}

// wait for cluster to be ready
mClusterWatch, err = client.GetManagementWatchInterface(management.ClusterType, metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "CreateAndImportK3DCluster: failed to wait for management cluster ready")
}

err = wait.WatchWait(mClusterWatch, func(event watch.Event) (bool, error) {
var mc v3.Cluster
err = runtime.DefaultUnstructuredConverter.FromUnstructured(event.Object.(*unstructured.Unstructured).Object, &mc)

_ = scheme.Scheme.Convert(event.Object, mgmtCluster, nil)

for _, cond := range mc.Status.Conditions {
if cond.Type == "Ready" && cond.Status == "True" {
return true, nil
}

break
}

return false, nil
})
if err != nil {
return nil, errors.Wrap(err, "CreateAndImportK3DCluster: failed to wait for management cluster ready")
}

return mgmtCluster, nil
}

// defaultName returns a random string if name is empty, otherwise name is returned unmodified.
func defaultName(name string) string {
if name == "" {
name, _ = randomtoken.Generate()
}

return name[:8]
}
20 changes: 14 additions & 6 deletions tests/framework/clients/rancher/client.go
Expand Up @@ -7,8 +7,9 @@ import (

frameworkDynamic "github.com/rancher/rancher/tests/framework/clients/dynamic"
management "github.com/rancher/rancher/tests/framework/clients/rancher/generated/management/v3"
provisioning "github.com/rancher/rancher/tests/framework/clients/rancher/provisioning"
"github.com/rancher/rancher/tests/framework/clients/rancher/provisioning"
"github.com/rancher/rancher/tests/framework/pkg/clientbase"
"github.com/rancher/rancher/tests/framework/pkg/config"
"github.com/rancher/rancher/tests/framework/pkg/session"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand All @@ -25,7 +26,14 @@ type Client struct {
Session *session.Session
}

func NewClient(bearerToken string, rancherConfig *Config, session *session.Session) (*Client, error) {
func NewClient(bearerToken string, session *session.Session) (*Client, error) {
rancherConfig := new(Config)
config.LoadConfig(ConfigurationFileKey, rancherConfig)

if bearerToken == "" {
bearerToken = rancherConfig.AdminToken
}

c := &Client{
RancherConfig: rancherConfig,
}
Expand Down Expand Up @@ -53,7 +61,7 @@ func NewClient(bearerToken string, rancherConfig *Config, session *session.Sessi

func newRestConfig(bearerToken string, rancherConfig *Config) *rest.Config {
return &rest.Config{
Host: rancherConfig.RancherHost,
Host: rancherConfig.Host,
BearerToken: bearerToken,
TLSClientConfig: rest.TLSClientConfig{
Insecure: *rancherConfig.Insecure,
Expand All @@ -64,7 +72,7 @@ func newRestConfig(bearerToken string, rancherConfig *Config) *rest.Config {

func clientOpts(restConfig *rest.Config, rancherConfig *Config) *clientbase.ClientOpts {
return &clientbase.ClientOpts{
URL: fmt.Sprintf("https://%s/v3", rancherConfig.RancherHost),
URL: fmt.Sprintf("https://%s/v3", rancherConfig.Host),
TokenKey: restConfig.BearerToken,
Insecure: restConfig.Insecure,
CACerts: rancherConfig.CACerts,
Expand All @@ -81,7 +89,7 @@ func (c *Client) AsUser(user *management.User) (*Client, error) {
return nil, err
}

return NewClient(returnedToken.Token, c.RancherConfig, c.Session)
return NewClient(returnedToken.Token, c.Session)
}

// GetRancherDynamicClient is a helper function that instantiates a dynamic client to communicate with the rancher host.
Expand Down Expand Up @@ -109,5 +117,5 @@ func (c *Client) GetManagementWatchInterface(schemaType string, opts metav1.List
return nil, err
}

return dynamicClient.Resource(groupVersionResource).Namespace("").Watch(context.TODO(), opts)
return dynamicClient.Resource(groupVersionResource).Watch(context.TODO(), opts)
}
10 changes: 5 additions & 5 deletions tests/framework/clients/rancher/config.go
Expand Up @@ -3,9 +3,9 @@ package rancher
const ConfigurationFileKey = "rancher"

type Config struct {
RancherHost string `yaml:"rancherHost"`
AdminToken string `yaml:"adminToken"`
Insecure *bool `yaml:"insecure" default:"true"`
CAFile string `yaml:"caFile" default:""`
CACerts string `yaml:"caCerts" default:""`
Host string `yaml:"host"`
AdminToken string `yaml:"adminToken"`
Insecure *bool `yaml:"insecure" default:"true"`
CAFile string `yaml:"caFile" default:""`
CACerts string `yaml:"caCerts" default:""`
}

0 comments on commit 793755a

Please sign in to comment.