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 }