Skip to content

Commit 2b5a51f

Browse files
committed
E2e: Implement LogCollector interface
CAPI provides an interface LogCollector that the providers can implement to get provider specific logs from the CAPI test specs. This gives us logs from the clusterctl upgrade, self-hosted and remediation tests. Moves the capo-e2e-image.tar out of the _artifacts since we do not need to save this with the logs. It just takes up space for no reason. Fix crictl commands to include the endpoint to use.
1 parent ed24d87 commit 2b5a51f

File tree

3 files changed

+131
-5
lines changed

3 files changed

+131
-5
lines changed

scripts/ci-e2e.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ fi
8686

8787
# Upload image for e2e clusterctl upgrade tests
8888
source "${REPO_ROOT}/hack/ci/${RESOURCE_TYPE}.sh"
89-
CONTAINER_ARCHIVE="${ARTIFACTS}/capo-e2e-image.tar"
89+
CONTAINER_ARCHIVE="/tmp/capo-e2e-image.tar"
9090
SSH_KEY="$(get_ssh_private_key_file)"
9191
SSH_ARGS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o PasswordAuthentication=no"
9292
CONTROLLER_IP=${CONTROLLER_IP:-"10.0.3.15"}

test/e2e/shared/common.go

Lines changed: 128 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ import (
3131
. "github.com/onsi/ginkgo/v2"
3232
. "github.com/onsi/gomega"
3333
corev1 "k8s.io/api/core/v1"
34+
"k8s.io/apimachinery/pkg/types"
3435
"k8s.io/apimachinery/pkg/util/sets"
36+
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
37+
expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
3538
"sigs.k8s.io/cluster-api/test/framework"
3639
"sigs.k8s.io/cluster-api/test/framework/clusterctl"
3740
"sigs.k8s.io/cluster-api/util"
@@ -199,15 +202,15 @@ func dumpMachine(ctx context.Context, e2eCtx *E2EContext, machine infrav1.OpenSt
199202
},
200203
{
201204
title: "containerd-info",
202-
cmd: "crictl info",
205+
cmd: "crictl --runtime-endpoint unix:///run/containerd/containerd.sock info",
203206
},
204207
{
205208
title: "containerd-containers",
206-
cmd: "crictl ps",
209+
cmd: "crictl --runtime-endpoint unix:///run/containerd/containerd.sock ps",
207210
},
208211
{
209212
title: "containerd-pods",
210-
cmd: "crictl pods",
213+
cmd: "crictl --runtime-endpoint unix:///run/containerd/containerd.sock pods",
211214
},
212215
{
213216
title: "cloud-final",
@@ -262,3 +265,125 @@ func SetEnvVar(key, value string, private bool) {
262265
Logf("Setting environment variable: key=%s, value=%s", key, printableValue)
263266
_ = os.Setenv(key, value)
264267
}
268+
269+
// getOpenStackClusterFromMachine gets the OpenStackCluster that is related to the given machine.
270+
func getOpenStackClusterFromMachine(ctx context.Context, client client.Client, machine *clusterv1.Machine) (*infrav1.OpenStackCluster, error) {
271+
key := types.NamespacedName{
272+
Namespace: machine.Namespace,
273+
Name: machine.Spec.ClusterName,
274+
}
275+
cluster := &clusterv1.Cluster{}
276+
err := client.Get(ctx, key, cluster)
277+
if err != nil {
278+
return nil, err
279+
}
280+
281+
key = types.NamespacedName{
282+
Namespace: cluster.Spec.InfrastructureRef.Namespace,
283+
Name: cluster.Spec.InfrastructureRef.Name,
284+
}
285+
openStackCluster := &infrav1.OpenStackCluster{}
286+
err = client.Get(ctx, key, openStackCluster)
287+
return openStackCluster, err
288+
}
289+
290+
type OpenStackLogCollector struct {
291+
E2EContext E2EContext
292+
}
293+
294+
// CollectMachineLog gets logs for the OpenStack resources related to the given machine.
295+
func (o OpenStackLogCollector) CollectMachineLog(ctx context.Context, managementClusterClient client.Client, m *clusterv1.Machine, outputPath string) error {
296+
machineLogBase := path.Join(outputPath, "instances", m.Namespace, m.Name)
297+
metaLog := path.Join(machineLogBase, "instance.log")
298+
299+
if err := os.MkdirAll(filepath.Dir(metaLog), 0o750); err != nil {
300+
_, _ = fmt.Fprintf(GinkgoWriter, "couldn't create directory %q for file: %s\n", metaLog, err)
301+
}
302+
303+
f, err := os.OpenFile(metaLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
304+
if err != nil {
305+
_, _ = fmt.Fprintf(GinkgoWriter, "couldn't open file %q: %s\n", metaLog, err)
306+
return nil
307+
}
308+
defer f.Close()
309+
310+
openStackCluster, err := getOpenStackClusterFromMachine(ctx, managementClusterClient, m)
311+
if err != nil {
312+
return fmt.Errorf("error getting OpenStackCluster for Machine: %s", err)
313+
}
314+
315+
if len(m.Status.Addresses) < 1 {
316+
return fmt.Errorf("unable to get logs for machine since it has no address")
317+
}
318+
ip := m.Status.Addresses[0].Address
319+
320+
srvs, err := GetOpenStackServers(&o.E2EContext, openStackCluster, sets.New(m.Spec.InfrastructureRef.Name))
321+
if err != nil {
322+
return fmt.Errorf("cannot dump machines, could not get servers from OpenStack: %v", err)
323+
}
324+
if len(srvs) != 1 {
325+
return fmt.Errorf("expected exactly 1 server but got %d", len(srvs))
326+
}
327+
srv := srvs[m.Spec.InfrastructureRef.Name]
328+
329+
serverJSON, err := json.MarshalIndent(srv, "", " ")
330+
if err != nil {
331+
return fmt.Errorf("error marshalling server %v: %s", srv, err)
332+
}
333+
if err := os.WriteFile(path.Join(machineLogBase, "server.txt"), serverJSON, 0o600); err != nil {
334+
return fmt.Errorf("error writing server JSON %s: %s", serverJSON, err)
335+
}
336+
_, _ = fmt.Fprintf(f, "instance found: %q\n", srv.ID)
337+
338+
srvUser := o.E2EContext.E2EConfig.GetVariable(SSHUserMachine)
339+
executeCommands(
340+
ctx,
341+
o.E2EContext.Settings.ArtifactFolder,
342+
o.E2EContext.Settings.Debug,
343+
filepath.Dir(f.Name()),
344+
ip,
345+
openStackCluster.Status.Bastion.FloatingIP,
346+
srvUser,
347+
[]command{
348+
// don't do this for now, it just takes to long
349+
// {
350+
// title: "systemd",
351+
// cmd: "journalctl --no-pager --output=short-precise | grep -v 'audit:\\|audit\\['",
352+
// },
353+
{
354+
title: "kern",
355+
cmd: "journalctl --no-pager --output=short-precise -k",
356+
},
357+
{
358+
title: "containerd-info",
359+
cmd: "crictl --runtime-endpoint unix:///run/containerd/containerd.sock info",
360+
},
361+
{
362+
title: "containerd-containers",
363+
cmd: "crictl --runtime-endpoint unix:///run/containerd/containerd.sock ps",
364+
},
365+
{
366+
title: "containerd-pods",
367+
cmd: "crictl --runtime-endpoint unix:///run/containerd/containerd.sock pods",
368+
},
369+
{
370+
title: "cloud-final",
371+
cmd: "journalctl --no-pager -u cloud-final",
372+
},
373+
{
374+
title: "kubelet",
375+
cmd: "journalctl --no-pager -u kubelet.service",
376+
},
377+
{
378+
title: "containerd",
379+
cmd: "journalctl --no-pager -u containerd.service",
380+
},
381+
},
382+
)
383+
return nil
384+
}
385+
386+
// CollectMachinePoolLog is not yet implemented for the OpenStack provider.
387+
func (o OpenStackLogCollector) CollectMachinePoolLog(_ context.Context, _ client.Client, _ *expv1.MachinePool, _ string) error {
388+
return fmt.Errorf("not implemented")
389+
}

test/e2e/shared/suite.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,8 @@ func AllNodesBeforeSuite(e2eCtx *E2EContext, data []byte) {
182182
e2eCtx.Settings.ArtifactFolder = conf.ArtifactFolder
183183
e2eCtx.Settings.ConfigPath = conf.ConfigPath
184184
e2eCtx.Environment.ClusterctlConfigPath = conf.ClusterctlConfigPath
185-
e2eCtx.Environment.BootstrapClusterProxy = framework.NewClusterProxy("bootstrap", conf.KubeconfigPath, e2eCtx.Environment.Scheme)
185+
withLogCollector := framework.WithMachineLogCollector(OpenStackLogCollector{E2EContext: *e2eCtx})
186+
e2eCtx.Environment.BootstrapClusterProxy = framework.NewClusterProxy("bootstrap", conf.KubeconfigPath, e2eCtx.Environment.Scheme, withLogCollector)
186187
e2eCtx.E2EConfig = &conf.E2EConfig
187188
e2eCtx.Settings.KubetestConfigFilePath = conf.KubetestConfigFilePath
188189
e2eCtx.Settings.UseCIArtifacts = conf.UseCIArtifacts

0 commit comments

Comments
 (0)