sigs.k8s.io/cluster-api-provider-aws@v1.5.5/test/e2e/shared/common.go (about)

     1  //go:build e2e
     2  // +build e2e
     3  
     4  /*
     5  Copyright 2020 The Kubernetes Authors.
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11  	http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    19  
    20  package shared
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  	"os"
    26  	"path"
    27  	"path/filepath"
    28  
    29  	. "github.com/onsi/ginkgo"
    30  	. "github.com/onsi/gomega"
    31  	corev1 "k8s.io/api/core/v1"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	crclient "sigs.k8s.io/controller-runtime/pkg/client"
    34  
    35  	infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1"
    36  	"sigs.k8s.io/cluster-api/test/framework"
    37  	"sigs.k8s.io/cluster-api/test/framework/clusterctl"
    38  	"sigs.k8s.io/cluster-api/util"
    39  )
    40  
    41  func SetupNamespace(ctx context.Context, specName string, e2eCtx *E2EContext) *corev1.Namespace {
    42  	Byf("Creating a namespace for hosting the %q test spec", specName)
    43  	namespace := framework.CreateNamespace(ctx, framework.CreateNamespaceInput{
    44  		Creator: e2eCtx.Environment.BootstrapClusterProxy.GetClient(),
    45  		Name:    fmt.Sprintf("%s-%s", specName, util.RandomString(6)),
    46  	})
    47  	return namespace
    48  }
    49  
    50  func SetupSpecNamespace(ctx context.Context, specName string, e2eCtx *E2EContext) *corev1.Namespace {
    51  	Byf("Creating a namespace for hosting the %q test spec", specName)
    52  	namespace, cancelWatches := framework.CreateNamespaceAndWatchEvents(ctx, framework.CreateNamespaceAndWatchEventsInput{
    53  		Creator:   e2eCtx.Environment.BootstrapClusterProxy.GetClient(),
    54  		ClientSet: e2eCtx.Environment.BootstrapClusterProxy.GetClientSet(),
    55  		Name:      fmt.Sprintf("%s-%s", specName, util.RandomString(6)),
    56  		LogFolder: filepath.Join(e2eCtx.Settings.ArtifactFolder, "clusters", e2eCtx.Environment.BootstrapClusterProxy.GetName()),
    57  	})
    58  
    59  	e2eCtx.Environment.Namespaces[namespace] = cancelWatches
    60  	Expect(e2eCtx.E2EConfig).ToNot(BeNil(), "Invalid argument. e2eConfig can't be nil")
    61  	Expect(e2eCtx.E2EConfig.Variables).To(HaveKey(KubernetesVersion))
    62  
    63  	return namespace
    64  }
    65  
    66  func DumpSpecResourcesAndCleanup(ctx context.Context, specName string, namespace *corev1.Namespace, e2eCtx *E2EContext) {
    67  	Byf("Dumping all the Cluster API resources in the %q namespace", namespace.Name)
    68  	// Dump all Cluster API related resources to artifacts before deleting them.
    69  	cancelWatches := e2eCtx.Environment.Namespaces[namespace]
    70  	DumpSpecResources(ctx, e2eCtx, namespace)
    71  	Byf("Dumping all EC2 instances in the %q namespace", namespace.Name)
    72  	DumpMachines(ctx, e2eCtx, namespace)
    73  	if !e2eCtx.Settings.SkipCleanup {
    74  		intervals := e2eCtx.E2EConfig.GetIntervals(specName, "wait-delete-cluster")
    75  		Byf("Deleting all clusters in the %q namespace with intervals %q", namespace.Name, intervals)
    76  		framework.DeleteAllClustersAndWait(ctx, framework.DeleteAllClustersAndWaitInput{
    77  			Client:    e2eCtx.Environment.BootstrapClusterProxy.GetClient(),
    78  			Namespace: namespace.Name,
    79  		}, intervals...)
    80  
    81  		Byf("Deleting namespace used for hosting the %q test spec", specName)
    82  		framework.DeleteNamespace(ctx, framework.DeleteNamespaceInput{
    83  			Deleter: e2eCtx.Environment.BootstrapClusterProxy.GetClient(),
    84  			Name:    namespace.Name,
    85  		})
    86  	}
    87  	if cancelWatches != nil {
    88  		cancelWatches()
    89  	}
    90  	delete(e2eCtx.Environment.Namespaces, namespace)
    91  }
    92  
    93  func DumpMachines(ctx context.Context, e2eCtx *E2EContext, namespace *corev1.Namespace) {
    94  	machines := MachinesForSpec(ctx, e2eCtx.Environment.BootstrapClusterProxy, namespace)
    95  	instances, err := allMachines(ctx, e2eCtx)
    96  	if err != nil {
    97  		return
    98  	}
    99  	instanceID := ""
   100  	for _, m := range machines.Items {
   101  		for _, i := range instances {
   102  			if i.name == m.Name {
   103  				instanceID = i.instanceID
   104  				break
   105  			}
   106  		}
   107  		if instanceID == "" {
   108  			return
   109  		}
   110  		DumpMachine(ctx, e2eCtx, m, instanceID, nil)
   111  	}
   112  }
   113  
   114  func DumpMachinesFromProxy(ctx context.Context, e2eCtx *E2EContext, namespace *corev1.Namespace, proxy framework.ClusterProxy) {
   115  	machines := MachinesForSpec(ctx, proxy, namespace)
   116  	instances, err := allMachines(ctx, e2eCtx)
   117  	if err != nil {
   118  		return
   119  	}
   120  	instanceID := ""
   121  	for _, m := range machines.Items {
   122  		for _, i := range instances {
   123  			if i.name == m.Name {
   124  				instanceID = i.instanceID
   125  				break
   126  			}
   127  		}
   128  		if instanceID == "" {
   129  			return
   130  		}
   131  		clusterName := proxy.GetName()
   132  		DumpMachine(ctx, e2eCtx, m, instanceID, &clusterName)
   133  	}
   134  }
   135  
   136  func MachinesForSpec(ctx context.Context, clusterProxy framework.ClusterProxy, namespace *corev1.Namespace) *infrav1.AWSMachineList {
   137  	lister := clusterProxy.GetClient()
   138  	list := new(infrav1.AWSMachineList)
   139  	if err := lister.List(ctx, list, crclient.InNamespace(namespace.GetName())); err != nil {
   140  		fmt.Fprintln(GinkgoWriter, "couldn't find machines")
   141  		return nil
   142  	}
   143  	return list
   144  }
   145  
   146  func DumpMachine(ctx context.Context, e2eCtx *E2EContext, machine infrav1.AWSMachine, instanceID string, cluster *string) {
   147  	logPath := filepath.Join(e2eCtx.Settings.ArtifactFolder, "clusters", e2eCtx.Environment.BootstrapClusterProxy.GetName())
   148  	if cluster != nil {
   149  		logPath = filepath.Join(e2eCtx.Settings.ArtifactFolder, "clusters", *cluster)
   150  	}
   151  	machineLogBase := path.Join(logPath, "instances", machine.Namespace, machine.Name)
   152  	metaLog := path.Join(machineLogBase, "instance.log")
   153  	if err := os.MkdirAll(filepath.Dir(metaLog), 0750); err != nil {
   154  		fmt.Fprintf(GinkgoWriter, "couldn't create directory for file: path=%s, err=%s", metaLog, err)
   155  	}
   156  	f, err := os.OpenFile(metaLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) //nolint:gosec
   157  	if err != nil {
   158  		return
   159  	}
   160  	defer f.Close() //nolint:gosec
   161  	fmt.Fprintf(f, "instance found: instance-id=%q\n", instanceID)
   162  	commandsForMachine(
   163  		ctx,
   164  		e2eCtx,
   165  		f,
   166  		instanceID,
   167  		[]command{
   168  			{
   169  				title: "systemd",
   170  				cmd:   "journalctl --no-pager --output=short-precise | grep -v  'audit:\\|audit\\['",
   171  			},
   172  			{
   173  				title: "kern",
   174  				cmd:   "journalctl --no-pager --output=short-precise -k",
   175  			},
   176  			{
   177  				title: "containerd-info",
   178  				cmd:   "crictl info",
   179  			},
   180  			{
   181  				title: "cloud-final",
   182  				cmd:   "journalctl --no-pager -u cloud-final",
   183  			},
   184  			{
   185  				title: "kubelet",
   186  				cmd:   "journalctl --no-pager -u kubelet.service",
   187  			},
   188  			{
   189  				title: "containerd",
   190  				cmd:   "journalctl --no-pager -u containerd.service",
   191  			},
   192  		},
   193  	)
   194  }
   195  
   196  func DumpSpecResources(ctx context.Context, e2eCtx *E2EContext, namespace *corev1.Namespace) {
   197  	framework.DumpAllResources(ctx, framework.DumpAllResourcesInput{
   198  		Lister:    e2eCtx.Environment.BootstrapClusterProxy.GetClient(),
   199  		Namespace: namespace.Name,
   200  		LogPath:   filepath.Join(e2eCtx.Settings.ArtifactFolder, "clusters", e2eCtx.Environment.BootstrapClusterProxy.GetName(), "resources"),
   201  	})
   202  }
   203  
   204  func DumpSpecResourcesFromProxy(ctx context.Context, e2eCtx *E2EContext, namespace *corev1.Namespace, proxy framework.ClusterProxy) {
   205  	framework.DumpAllResources(ctx, framework.DumpAllResourcesInput{
   206  		Lister:    proxy.GetClient(),
   207  		Namespace: namespace.Name,
   208  		LogPath:   filepath.Join(e2eCtx.Settings.ArtifactFolder, "clusters", proxy.GetName(), "resources"),
   209  	})
   210  }
   211  
   212  func Byf(format string, a ...interface{}) {
   213  	By(fmt.Sprintf(format, a...))
   214  }
   215  
   216  // ConditionFn returns true if a condition exists.
   217  type ConditionFn func() bool
   218  
   219  // ConditionalIt will only perform the It block if the condition function returns true
   220  // Inspired from Cilium: https://github.com/cilium/cilium/blob/03bfb2bece5108549b3d613e119059758035d448/test/ginkgo-ext/scopes.go#L658
   221  func ConditionalIt(conditionFn ConditionFn, text string, body func()) bool {
   222  	if conditionFn() {
   223  		return It(text, body)
   224  	}
   225  
   226  	return It(text, func() {
   227  		Skip("skipping due to unmet condition")
   228  	})
   229  }
   230  
   231  // LoadE2EConfig loads the e2econfig from the specified path.
   232  func LoadE2EConfig(configPath string) *clusterctl.E2EConfig {
   233  	//TODO: This is commented out as it assumes kubeadm and errors if its not there
   234  	// Remove localLoadE2EConfig and use the line below when this issue is resolved:
   235  	// https://github.com/kubernetes-sigs/cluster-api/issues/3983
   236  	// config := clusterctl.LoadE2EConfig(context.TODO(), clusterctl.LoadE2EConfigInput{ConfigPath: configPath})
   237  	config := localLoadE2EConfig(configPath)
   238  
   239  	Expect(config).ToNot(BeNil(), "Failed to load E2E config from %s", configPath)
   240  	return config
   241  }
   242  
   243  // SetEnvVar sets an environment variable in the process. If marked private,
   244  // the value is not printed.
   245  func SetEnvVar(key, value string, private bool) {
   246  	printableValue := "*******"
   247  	if !private {
   248  		printableValue = value
   249  	}
   250  
   251  	Byf("Setting environment variable: key=%s, value=%s", key, printableValue)
   252  	os.Setenv(key, value)
   253  }
   254  
   255  func CreateAWSClusterControllerIdentity(k8sclient crclient.Client) {
   256  	controllerIdentity := &infrav1.AWSClusterControllerIdentity{
   257  		TypeMeta: metav1.TypeMeta{
   258  			APIVersion: infrav1.GroupVersion.String(),
   259  			Kind:       string(infrav1.ControllerIdentityKind),
   260  		},
   261  		ObjectMeta: metav1.ObjectMeta{
   262  			Name: infrav1.AWSClusterControllerIdentityName,
   263  		},
   264  		Spec: infrav1.AWSClusterControllerIdentitySpec{
   265  			AWSClusterIdentitySpec: infrav1.AWSClusterIdentitySpec{
   266  				AllowedNamespaces: &infrav1.AllowedNamespaces{},
   267  			},
   268  		},
   269  	}
   270  	_ = k8sclient.Create(context.TODO(), controllerIdentity)
   271  }