-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Azure e2e template tests #300
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -15,23 +15,31 @@ | |||||
package kubeclient | ||||||
|
||||||
import ( | ||||||
"bufio" | ||||||
"bytes" | ||||||
"context" | ||||||
"fmt" | ||||||
"io" | ||||||
"os" | ||||||
"os/exec" | ||||||
"path/filepath" | ||||||
|
||||||
"github.com/Mirantis/hmc/test/utils" | ||||||
"github.com/a8m/envsubst" | ||||||
. "github.com/onsi/ginkgo/v2" | ||||||
corev1 "k8s.io/api/core/v1" | ||||||
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" | ||||||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||||||
"k8s.io/apimachinery/pkg/runtime/schema" | ||||||
"k8s.io/apimachinery/pkg/runtime/serializer/yaml" | ||||||
yamlutil "k8s.io/apimachinery/pkg/util/yaml" | ||||||
"k8s.io/client-go/discovery" | ||||||
"k8s.io/client-go/dynamic" | ||||||
"k8s.io/client-go/kubernetes" | ||||||
"k8s.io/client-go/rest" | ||||||
"k8s.io/client-go/restmapper" | ||||||
"k8s.io/client-go/tools/clientcmd" | ||||||
) | ||||||
|
||||||
|
@@ -123,6 +131,75 @@ func new(configBytes []byte, namespace string) (*KubeClient, error) { | |||||
}, nil | ||||||
} | ||||||
|
||||||
func (kc *KubeClient) CreateAzureCredentialsKubeSecret(ctx context.Context) error { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks more complex than it should be. @squizzi already proposed several fixes and I tend to agree with him |
||||||
serializer := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) | ||||||
yamlFile, err := os.ReadFile("./config/dev/azure-credentials.yaml") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of calling |
||||||
|
||||||
if err != nil { | ||||||
return fmt.Errorf("failed to read azure credential file: %w", err) | ||||||
} | ||||||
|
||||||
yamlFile, err = envsubst.Bytes(yamlFile) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you're going to perform substitutions on the file it should probably be a |
||||||
if err != nil { | ||||||
return fmt.Errorf("failed to process azure credential file: %w", err) | ||||||
} | ||||||
|
||||||
c := discovery.NewDiscoveryClientForConfigOrDie(kc.Config) | ||||||
groupResources, err := restmapper.GetAPIGroupResources(c) | ||||||
if err != nil { | ||||||
return fmt.Errorf("failed to fetch group resources: %w", err) | ||||||
} | ||||||
|
||||||
yamlReader := yamlutil.NewYAMLReader(bufio.NewReader(bytes.NewReader(yamlFile))) | ||||||
for { | ||||||
yamlDoc, err := yamlReader.Read() | ||||||
|
||||||
if err != nil { | ||||||
if err == io.EOF { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
break | ||||||
} | ||||||
return fmt.Errorf("failed to process azure credential file: %w", err) | ||||||
|
||||||
} | ||||||
|
||||||
credentialResource := &unstructured.Unstructured{} | ||||||
_, _, err = serializer.Decode(yamlDoc, nil, credentialResource) | ||||||
if err != nil { | ||||||
return fmt.Errorf("failed to deserialize azure credential object: %w", err) | ||||||
} | ||||||
|
||||||
mapper := restmapper.NewDiscoveryRESTMapper(groupResources) | ||||||
mapping, err := mapper.RESTMapping(credentialResource.GroupVersionKind().GroupKind()) | ||||||
|
||||||
if err != nil { | ||||||
return fmt.Errorf("failed to create rest mapper: %w", err) | ||||||
} | ||||||
|
||||||
dc, err := kc.GetDynamicClient(schema.GroupVersionResource{ | ||||||
Group: credentialResource.GroupVersionKind().Group, | ||||||
Version: credentialResource.GroupVersionKind().Version, | ||||||
Resource: mapping.Resource.Resource, | ||||||
}) | ||||||
|
||||||
if err != nil { | ||||||
return fmt.Errorf("failed to create dynamic client: %w", err) | ||||||
} | ||||||
|
||||||
exists, err := dc.Get(ctx, credentialResource.GetName(), metav1.GetOptions{}) | ||||||
if err != nil && !apierrors.IsNotFound(err) { | ||||||
return fmt.Errorf("failed to check for existing credential: %w", err) | ||||||
} | ||||||
|
||||||
if exists == nil { | ||||||
if _, err = dc.Create(ctx, credentialResource, metav1.CreateOptions{}); err != nil { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You don't even need to call |
||||||
return fmt.Errorf("failed to create azure credentials: %w", err) | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
return nil | ||||||
} | ||||||
|
||||||
// CreateAWSCredentialsKubeSecret uses clusterawsadm to encode existing AWS | ||||||
// credentials and create a secret in the given namespace if one does not | ||||||
// already exist. | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,8 +40,10 @@ const ( | |
type Template string | ||
|
||
const ( | ||
TemplateAWSStandaloneCP Template = "aws-standalone-cp" | ||
TemplateAWSHostedCP Template = "aws-hosted-cp" | ||
TemplateAWSStandaloneCP Template = "aws-standalone-cp" | ||
TemplateAWSHostedCP Template = "aws-hosted-cp" | ||
TemplateAzureHostedCP Template = "azure-hosted-cp" | ||
TemplateAzureStandaloneCP Template = "azure-standalone-cp" | ||
) | ||
|
||
//go:embed resources/aws-standalone-cp.yaml.tpl | ||
|
@@ -50,16 +52,22 @@ var awsStandaloneCPManagedClusterTemplateBytes []byte | |
//go:embed resources/aws-hosted-cp.yaml.tpl | ||
var awsHostedCPManagedClusterTemplateBytes []byte | ||
|
||
//go:embed resources/azure-standalone-cp.yaml.tpl | ||
var azureStandaloneCPManagedClusterTemplateBytes []byte | ||
|
||
//go:embed resources/azure-hosted-cp.yaml.tpl | ||
var azureHostedCPManagedClusterTemplateBytes []byte | ||
|
||
func GetProviderLabel(provider ProviderType) string { | ||
return fmt.Sprintf("%s=%s", providerLabel, provider) | ||
} | ||
|
||
// GetUnstructured returns an unstructured ManagedCluster object based on the | ||
// provider and template. | ||
func GetUnstructured(provider ProviderType, templateName Template) *unstructured.Unstructured { | ||
func GetUnstructured(provider ProviderType, templateName Template, namespace string) *unstructured.Unstructured { | ||
GinkgoHelper() | ||
|
||
generatedName := uuid.New().String()[:8] + "-e2e-test" | ||
generatedName := "e2etest-" + uuid.New().String()[:8] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that this name doesn't align with the one ran in CI, if you want to change that name to make sure you modify |
||
_, _ = fmt.Fprintf(GinkgoWriter, "Generated cluster name: %q\n", generatedName) | ||
|
||
switch provider { | ||
|
@@ -84,6 +92,30 @@ func GetUnstructured(provider ProviderType, templateName Template) *unstructured | |
err = yaml.Unmarshal(managedClusterConfigBytes, &managedClusterConfig) | ||
Expect(err).NotTo(HaveOccurred(), "failed to unmarshal deployment config") | ||
|
||
return &unstructured.Unstructured{Object: managedClusterConfig} | ||
|
||
case ProviderAzure: | ||
Expect(os.Setenv("MANAGED_CLUSTER_NAME", generatedName)).NotTo(HaveOccurred()) | ||
Expect(os.Setenv("NAMESPACE", namespace)).NotTo(HaveOccurred()) | ||
|
||
var managedClusterTemplateBytes []byte | ||
switch templateName { | ||
case TemplateAzureHostedCP: | ||
managedClusterTemplateBytes = azureHostedCPManagedClusterTemplateBytes | ||
case TemplateAzureStandaloneCP: | ||
managedClusterTemplateBytes = azureStandaloneCPManagedClusterTemplateBytes | ||
default: | ||
Fail(fmt.Sprintf("unsupported Azure template: %s", templateName)) | ||
} | ||
|
||
managedClusterConfigBytes, err := envsubst.Bytes(managedClusterTemplateBytes) | ||
Expect(err).NotTo(HaveOccurred(), "failed to substitute environment variables") | ||
|
||
var managedClusterConfig map[string]interface{} | ||
|
||
err = yaml.Unmarshal(managedClusterConfigBytes, &managedClusterConfig) | ||
Expect(err).NotTo(HaveOccurred(), "failed to unmarshal deployment config") | ||
|
||
return &unstructured.Unstructured{Object: managedClusterConfig} | ||
default: | ||
Fail(fmt.Sprintf("unsupported provider: %s", provider)) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
apiVersion: hmc.mirantis.com/v1alpha1 | ||
kind: ManagedCluster | ||
metadata: | ||
name: ${MANAGED_CLUSTER_NAME} | ||
namespace: ${NAMESPACE} | ||
spec: | ||
template: azure-hosted-cp | ||
config: | ||
controlPlaneNumber: 1 | ||
workersNumber: 1 | ||
location: "westus" | ||
subscriptionID: "${AZURE_SUBSCRIPTION_ID}" | ||
controlPlane: | ||
vmSize: Standard_A4_v2 | ||
worker: | ||
vmSize: Standard_A4_v2 | ||
clusterIdentity: | ||
name: azure-cluster-identity | ||
namespace: ${NAMESPACE} | ||
tenantID: "${AZURE_TENANT_ID}" | ||
clientID: "${AZURE_CLIENT_ID}" | ||
clientSecret: "${AZURE_CLIENT_SECRET}" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
apiVersion: hmc.mirantis.com/v1alpha1 | ||
kind: ManagedCluster | ||
metadata: | ||
name: ${MANAGED_CLUSTER_NAME} | ||
namespace: ${NAMESPACE} | ||
spec: | ||
template: azure-standalone-cp | ||
config: | ||
controlPlaneNumber: 1 | ||
workersNumber: 1 | ||
location: "westus" | ||
subscriptionID: "${AZURE_SUBSCRIPTION_ID}" | ||
controlPlane: | ||
vmSize: Standard_A4_v2 | ||
worker: | ||
vmSize: Standard_A4_v2 | ||
clusterIdentity: | ||
name: azure-cluster-identity | ||
namespace: ${NAMESPACE} | ||
tenantID: "${AZURE_TENANT_ID}" | ||
clientID: "${AZURE_CLIENT_ID}" | ||
clientSecret: "${AZURE_CLIENT_SECRET}" |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,8 @@ import ( | |
"errors" | ||
"fmt" | ||
|
||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
|
||
"github.com/Mirantis/hmc/test/kubeclient" | ||
"github.com/Mirantis/hmc/test/utils" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
|
@@ -41,7 +43,7 @@ func VerifyProviderDeleted(ctx context.Context, kc *kubeclient.KubeClient, clust | |
func validateClusterDeleted(ctx context.Context, kc *kubeclient.KubeClient, clusterName string) error { | ||
// Validate that the Cluster resource has been deleted | ||
cluster, err := kc.GetCluster(ctx, clusterName) | ||
if err != nil { | ||
if err != nil && !apierrors.IsNotFound(err) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
return err | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're creating hosted cluster template in the local kind cluster. It should fail. And if it's not - the test should be reworked