Skip to content

Commit

Permalink
set debugContainerImage to ghcr image
Browse files Browse the repository at this point in the history
Signed-off-by: Maartje Eyskens <[email protected]>
  • Loading branch information
meyskens committed Dec 19, 2024
1 parent 536cd1c commit e8d1c72
Showing 1 changed file with 192 additions and 2 deletions.
194 changes: 192 additions & 2 deletions cmd/cofidectl/cmd/workload/workload.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,24 @@
package workload

import (
"bytes"
"context"
"fmt"
"io"
"os"
"time"

trust_zone_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_zone/v1alpha1"
cmdcontext "github.com/cofide/cofidectl/cmd/cofidectl/cmd/context"
"github.com/cofide/cofidectl/internal/pkg/workload"
"github.com/olekukonko/tablewriter"

"github.com/briandowns/spinner"
kubeutil "github.com/cofide/cofidectl/internal/pkg/kube"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/rand"
)

type WorkloadCommand struct {
Expand All @@ -29,6 +38,13 @@ var workloadRootCmdDesc = `
This command consists of multiple sub-commands to interact with workloads.
`

type Opts struct {
workloadName string // used to pass arg[0]
podName string
namespace string
trustZone string
}

func (c *WorkloadCommand) GetRootCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "workload list|discover [ARGS]",
Expand All @@ -40,6 +56,7 @@ func (c *WorkloadCommand) GetRootCommand() *cobra.Command {
cmd.AddCommand(
c.GetListCommand(),
c.GetDiscoverCommand(),
c.GetStatusCommand(),
)

return cmd
Expand Down Expand Up @@ -87,22 +104,73 @@ func (w *WorkloadCommand) GetListCommand() *cobra.Command {
return fmt.Errorf("no trust zones have been configured")
}

return nil
},
}

return cmd
}

var workloadStatusCmdDesc = `
This command will display the status of workloads in the Cofide configuration state.
`

func (w *WorkloadCommand) GetStatusCommand() *cobra.Command {
opts := Opts{}
cmd := &cobra.Command{
Use: "status [NAME]",
Short: "Display workload status",
Long: workloadStatusCmdDesc,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
kubeConfig, err := cmd.Flags().GetString("kube-config")
if err != nil {
return fmt.Errorf("failed to retrieve the kubeconfig file location")
}

ds, err := w.cmdCtx.PluginManager.GetDataSource()
if err != nil {
return err
}

var trustZones []*trust_zone_proto.TrustZone

if opts.trustZone != "" {
trustZone, err := ds.GetTrustZone(opts.trustZone)
if err != nil {
return err
}

trustZones = append(trustZones, trustZone)
} else {
trustZones, err = ds.ListTrustZones()
if err != nil {
return err
}
}

if len(trustZones) == 0 {
return fmt.Errorf("no trust zones have been configured")
}

err = renderRegisteredWorkloads(cmd.Context(), kubeConfig, trustZones)
if err != nil {
return err
}

return nil
opts.workloadName = args[0]
return w.status(cmd.Context(), kubeConfig, opts)
},
}

f := cmd.Flags()
f.StringVar(&opts.trustZone, "trust-zone", "", "list the registered workloads in a specific trust zone")
f.StringVar(&opts.podName, "pod-name", "", "Pod name for the workload")
f.StringVar(&opts.namespace, "namespace", "", "Namespace for the workload")
f.StringVar(&opts.trustZone, "trust-zone", "", "Trust zone for the workload")

cobra.CheckErr(cmd.MarkFlagRequired("pod-name"))
cobra.CheckErr(cmd.MarkFlagRequired("namespace"))
cobra.CheckErr(cmd.MarkFlagRequired("trust-zone"))

return cmd
}
Expand Down Expand Up @@ -137,6 +205,48 @@ func renderRegisteredWorkloads(ctx context.Context, kubeConfig string, trustZone
return nil
}

const debugContainerNamePrefix = "cofidectl-debug"
const debugContainerImage = "ghcr.io/cofide/cofidectl-debug-container/cmd:v0.1.0"

func (w *WorkloadCommand) status(ctx context.Context, kubeConfig string, opts Opts) error {
ds, err := w.cmdCtx.PluginManager.GetDataSource()
if err != nil {
return err
}

trustZone, err := ds.GetTrustZone(opts.trustZone)
if err != nil {
return err
}

client, err := kubeutil.NewKubeClientFromSpecifiedContext(kubeConfig, *trustZone.KubernetesContext)
if err != nil {
return err
}

// Create a spinner to display whilst the debug container is created and executed and logs retrieved
s := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
s.Start()
defer s.Stop()
s.Suffix = "Starting debug container"

pod, container, err := createDebugContainer(ctx, client, opts.podName, opts.namespace)
if err != nil {
return fmt.Errorf("could not create ephemeral debug container: %s", err)
}

s.Suffix = "Retrieving workload status"

workload, err := getWorkloadStatus(ctx, client, pod, container)
if err != nil {
return fmt.Errorf("could not retrieve logs of the ephemeral debug container: %w", err)
}

fmt.Println(workload)

return nil
}

var workloadDiscoverCmdDesc = `
This command will discover all of the unregistered workloads.
`
Expand Down Expand Up @@ -237,3 +347,83 @@ func renderUnregisteredWorkloads(ctx context.Context, kubeConfig string, trustZo

return nil
}

func createDebugContainer(ctx context.Context, client *kubeutil.Client, podName string, namespace string) (*corev1.Pod, string, error) {
pod, err := client.Clientset.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{})
if err != nil {
return nil, "", fmt.Errorf("error getting pod: %v", err)
}

debugContainerName := fmt.Sprintf("%s-%s", debugContainerNamePrefix, rand.String(5))

debugContainer := corev1.EphemeralContainer{
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
Name: debugContainerName,
Image: debugContainerImage,
ImagePullPolicy: corev1.PullIfNotPresent,
TTY: true,
Stdin: true,
VolumeMounts: []corev1.VolumeMount{
{
ReadOnly: true,
Name: "spiffe-workload-api",
MountPath: "/spiffe-workload-api",
}},
},
TargetContainerName: pod.Spec.Containers[0].Name,
}

pod.Spec.EphemeralContainers = append(pod.Spec.EphemeralContainers, debugContainer)

_, err = client.Clientset.CoreV1().Pods(namespace).UpdateEphemeralContainers(
ctx,
pod.Name,
pod,
metav1.UpdateOptions{},
)
if err != nil {
return nil, "", fmt.Errorf("error creating debug container: %v", err)
}

// Wait for the debug container to complete
waitCtx, cancel := context.WithTimeout(ctx, 5*time.Minute)
defer cancel()
for {
pod, err := client.Clientset.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{})
if err != nil {
return nil, "", fmt.Errorf("error getting pod status: %v", err)
}

for _, status := range pod.Status.EphemeralContainerStatuses {
if status.Name == debugContainerName && status.State.Terminated != nil {
return pod, debugContainerName, nil
}
}

select {
case <-waitCtx.Done():
return nil, "", fmt.Errorf("timeout waiting for debug container to complete")
default:
continue
}
}
}

func getWorkloadStatus(ctx context.Context, client *kubeutil.Client, pod *corev1.Pod, container string) (string, error) {
logs, err := client.Clientset.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &corev1.PodLogOptions{
Container: container,
}).Stream(ctx)
if err != nil {
return "", err
}
defer logs.Close()

// Read the logs
buf := new(bytes.Buffer)
_, err = io.Copy(buf, logs)
if err != nil {
return "", err
}

return buf.String(), nil
}

0 comments on commit e8d1c72

Please sign in to comment.