Skip to content

Commit

Permalink
Merge pull request #197 from susesgartner/rke2-templates
Browse files Browse the repository at this point in the history
RKE2 template test
  • Loading branch information
slickwarren authored Jun 13, 2024
2 parents 0888ef4 + 9010305 commit 3981d20
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 2 deletions.
129 changes: 129 additions & 0 deletions extensions/charts/rke2template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package charts

import (
"context"

"github.com/rancher/shepherd/clients/rancher"
"github.com/rancher/shepherd/extensions/cloudcredentials"
"github.com/rancher/shepherd/extensions/clusters"
"github.com/rancher/shepherd/extensions/defaults"
"github.com/rancher/shepherd/extensions/projects"
"github.com/rancher/shepherd/extensions/wait"
"github.com/rancher/shepherd/pkg/api/steve/catalog/types"
"github.com/rancher/shepherd/pkg/namegenerator"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
fleetNamespace = "fleet-default"
localCluster = "local"
)

// InstallTemplateChart installs a template from a repo.
func InstallTemplateChart(client *rancher.Client, repoName, templateName, clusterName, k8sVersion string, credentials *cloudcredentials.CloudCredential) error {
latestVersion, err := client.Catalog.GetLatestChartVersion(templateName, repoName)
if err != nil {
return err
}

project, err := projects.GetProjectByName(client, localCluster, "System")
if err != nil {
return err
}

installOptions := &InstallOptions{
Cluster: &clusters.ClusterMeta{
ID: localCluster,
},
Version: latestVersion,
ProjectID: project.ID,
}

serverSetting, err := client.Management.Setting.ByID(serverURLSettingID)
if err != nil {
return err
}

registrySetting, err := client.Management.Setting.ByID(defaultRegistrySettingID)
if err != nil {
return err
}

chartInstallActionPayload := &payloadOpts{
InstallOptions: *installOptions,
Name: templateName,
Namespace: fleetNamespace,
Host: serverSetting.Value,
DefaultRegistry: registrySetting.Value,
}

chartValues, err := client.Catalog.GetChartValues(repoName, templateName, installOptions.Version)
if err != nil {
return err
}

chartInstallAction := TemplateInstallAction(chartInstallActionPayload, repoName, clusterName, credentials.ID, k8sVersion, fleetNamespace, chartValues)

catalogClient, err := client.GetClusterCatalogClient(installOptions.Cluster.ID)
if err != nil {
return err
}

err = client.Catalog.InstallChart(chartInstallAction, repoName)
if err != nil {
return err
}

client.Session.RegisterCleanupFunc(func() error {
err := client.Catalog.UninstallChart(templateName, fleetNamespace, newChartUninstallAction())
if err != nil {
return err
}

watchAppInterface, err := catalogClient.Apps(fleetNamespace).Watch(context.TODO(), metav1.ListOptions{
FieldSelector: "metadata.name=" + templateName,
TimeoutSeconds: &defaults.WatchTimeoutSeconds,
})
if err != nil {
return err
}

err = wait.ResourceDelete(watchAppInterface)
if err != nil {
return err
}

return nil
})

err = VerifyChartInstall(catalogClient, fleetNamespace, templateName)
if err != nil {
return err
}
return err
}

// TemplateInstallAction creates the payload used when installing a template chart
func TemplateInstallAction(InstallActionPayload *payloadOpts, repoName, clusterName, cloudCredential, k8sVersion, namespace string, chartValues map[string]any) *types.ChartInstallAction {
chartValues["cloudCredentialSecretName"] = cloudCredential
chartValues["kubernetesVersion"] = k8sVersion
chartValues["cluster"].(map[string]any)["name"] = clusterName

for index := range chartValues["nodepools"].(map[string]any) {
chartValues["nodepools"].(map[string]any)[index].(map[string]any)["name"] = namegenerator.AppendRandomString("nodepool")
}

chartInstall := newChartInstall(
InstallActionPayload.Name,
InstallActionPayload.InstallOptions.Version,
InstallActionPayload.InstallOptions.Cluster.ID,
InstallActionPayload.InstallOptions.Cluster.Name,
InstallActionPayload.Host,
repoName,
InstallActionPayload.InstallOptions.ProjectID,
InstallActionPayload.DefaultRegistry,
chartValues)
chartInstalls := []types.ChartInstall{*chartInstall}

return newChartInstallAction(namespace, InstallActionPayload.ProjectID, chartInstalls)
}
3 changes: 3 additions & 0 deletions extensions/defaults/stevetypes/stevetypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ const (
Provisioning = "provisioning.cattle.io.cluster"
EtcdSnapshot = "rke.cattle.io.etcdsnapshot"
FleetCluster = "fleet.cattle.io.cluster"
ClusterRepo = "catalog.cattle.io.clusterrepo"
Apps = "catalog.cattle.io.apps"
Secret = "secret"
)
6 changes: 4 additions & 2 deletions extensions/provisioning/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,10 @@ func VerifyCluster(t *testing.T, client *rancher.Client, clustersConfig *cluster
podErrors := pods.StatusPods(client, status.ClusterName)
assert.Empty(t, podErrors)

if clustersConfig.ClusterSSHTests != nil {
VerifySSHTests(t, client, cluster, clustersConfig.ClusterSSHTests, status.ClusterName)
if clustersConfig != nil {
if clustersConfig.ClusterSSHTests != nil {
VerifySSHTests(t, client, cluster, clustersConfig.ClusterSSHTests, status.ClusterName)
}
}
}

Expand Down
7 changes: 7 additions & 0 deletions extensions/provisioninginput/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package provisioninginput

import (
v1 "github.com/rancher/rancher/pkg/apis/catalog.cattle.io/v1"
rkev1 "github.com/rancher/rancher/pkg/apis/rke.cattle.io/v1"
management "github.com/rancher/shepherd/clients/rancher/generated/management/v3"
"github.com/rancher/shepherd/extensions/machinepools"
Expand Down Expand Up @@ -223,3 +224,9 @@ type Config struct {
ClusterSSHTests []SSHTestCase `json:"clusterSSHTests,omitempty" yaml:"clusterSSHTests,omitempty"`
CRIDockerd bool `json:"criDockerd,omitempty" yaml:"criDockerd,omitempty"`
}

type TemplateConfig struct {
Repo *v1.ClusterRepo `json:"repo,omitempty" yaml:"repo,omitempty"`
TemplateName string `json:"templateName,omitempty" yaml:"templateName,omitempty"`
TemplateProvider string `json:"templateProvider,omitempty" yaml:"templateProvider,omitempty"`
}
44 changes: 44 additions & 0 deletions extensions/steve/steve.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import (
"time"

"github.com/rancher/shepherd/clients/rancher"
v1 "github.com/rancher/shepherd/clients/rancher/v1"
"github.com/sirupsen/logrus"
kwait "k8s.io/apimachinery/pkg/util/wait"
)

const (
notFound = "not found"
active = "active"
)

// WaitForSteveResourceDeletion accepts a client, steve resource type, and resource ID, then waits for a steve resource to be deleted
Expand All @@ -36,3 +38,45 @@ func WaitForSteveResourceDeletion(client *rancher.Client, interval, timeout time

return nil
}

// WaitForSteveResourceCreation waits for a given steve object to be created/come up active.
func WaitForSteveResourceCreation(client *rancher.Client, v1Resource *v1.SteveAPIObject, interval, timeout time.Duration) error {
err := kwait.PollUntilContextTimeout(context.TODO(), interval, timeout, true, func(ctx context.Context) (done bool, err error) {
client, err = client.ReLogin()
if err != nil {
return false, err
}

clusterResp, err := client.Steve.SteveType(v1Resource.Type).ByID(v1Resource.ID)
if err != nil {
return false, err
}

if clusterResp.ObjectMeta.State.Name == active {
logrus.Infof("%s(%s) is active", v1Resource.Kind, v1Resource.Name)
return true, nil
}

return false, nil
})

return err
}

// CreateResource creates a steve resource and polls the resulting object until it comes up active.
func CreateAndWaitForResource(client *rancher.Client, v1ResourceType string, v1Resource any, poll bool, interval, timeout time.Duration) (*v1.SteveAPIObject, error) {
resource, err := client.Steve.SteveType(v1ResourceType).Create(v1Resource)
if err != nil {
return nil, err
}
logrus.Infof("Creating %s(%s)", resource.Kind, resource.Name)

if poll {
err := WaitForSteveResourceCreation(client, resource, interval, timeout)
if err != nil {
return resource, err
}
}

return resource, nil
}
37 changes: 37 additions & 0 deletions extensions/wait/wait.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package wait

import (
"github.com/rancher/shepherd/pkg/wait"
"k8s.io/apimachinery/pkg/watch"
)

// ResourceCreate is a generic wait function for create operations on v1 resources
func ResourceCreate(watchInterface watch.Interface) error {
err := wait.WatchWait(watchInterface, func(event watch.Event) (ready bool, err error) {
if event.Type == watch.Added {
return true, nil
} else if event.Type == watch.Error {
return false, nil
}

return false, nil
})

return err

}

// ResourceCreate is a generic wait function for delete operations on v1 resources
func ResourceDelete(watchInterface watch.Interface) error {
err := wait.WatchWait(watchInterface, func(event watch.Event) (ready bool, err error) {
if event.Type == watch.Error {
return false, nil
} else if event.Type == watch.Deleted {
return true, nil
}

return false, nil
})

return err
}

0 comments on commit 3981d20

Please sign in to comment.