Skip to content

Commit

Permalink
Add multi-cluster support to bundle status cmd
Browse files Browse the repository at this point in the history
Signed-off-by: Stefan Prodan <[email protected]>
  • Loading branch information
stefanprodan committed Nov 18, 2023
1 parent 9a67eca commit 330b84e
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 38 deletions.
106 changes: 69 additions & 37 deletions cmd/timoni/bundle_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"errors"
"fmt"
"maps"

"cuelang.org/go/cue/cuecontext"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -75,65 +76,96 @@ func runBundleStatusCmd(cmd *cobra.Command, args []string) error {
bundleStatusArgs.name = args[0]
}

rm, err := runtime.NewResourceManager(kubeconfigArgs)
if err != nil {
return err
}
runtimeValues := make(map[string]string)

ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
if bundleArgs.runtimeFromEnv {
maps.Copy(runtimeValues, engine.GetEnv())
}

sm := runtime.NewStorageManager(rm)
instances, err := sm.List(ctx, "", bundleStatusArgs.name)
rt, err := buildRuntime(bundleArgs.runtimeFiles)
if err != nil {
return err
}

if len(instances) == 0 {
return fmt.Errorf("no instances found in bundle")
clusters := rt.SelectClusters(bundleArgs.runtimeCluster, bundleArgs.runtimeClusterGroup)
if len(clusters) == 0 {
return fmt.Errorf("no cluster found")
}

for _, instance := range instances {
log := LoggerBundleInstance(ctx, bundleStatusArgs.name, apiv1.RuntimeDefaultName, instance.Name)

log.Info(fmt.Sprintf("last applied %s",
colorizeSubject(instance.LastTransitionTime)))
log.Info(fmt.Sprintf("module %s",
colorizeSubject(instance.Module.Repository+":"+instance.Module.Version)))
log.Info(fmt.Sprintf("digest %s",
colorizeSubject(instance.Module.Digest)))
failed := false
for _, cluster := range clusters {
kubeconfigArgs.Context = &cluster.KubeContext

for _, image := range instance.Images {
log.Info(fmt.Sprintf("container image %s",
colorizeSubject(image)))
rm, err := runtime.NewResourceManager(kubeconfigArgs)
if err != nil {
return err
}

im := runtime.InstanceManager{Instance: apiv1.Instance{Inventory: instance.Inventory}}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()

objects, err := im.ListObjects()
sm := runtime.NewStorageManager(rm)
instances, err := sm.List(ctx, "", bundleStatusArgs.name)
if err != nil {
return err
}

for _, obj := range objects {
err = rm.Client().Get(ctx, client.ObjectKeyFromObject(obj), obj)
log := LoggerBundle(ctx, bundleStatusArgs.name, cluster.Name)

if len(instances) == 0 {
log.Error(nil, "no instances found in bundle")
failed = true
continue
}

for _, instance := range instances {
log := LoggerBundleInstance(ctx, bundleStatusArgs.name, cluster.Name, instance.Name)

log.Info(fmt.Sprintf("last applied %s",
colorizeSubject(instance.LastTransitionTime)))
log.Info(fmt.Sprintf("module %s",
colorizeSubject(instance.Module.Repository+":"+instance.Module.Version)))
log.Info(fmt.Sprintf("digest %s",
colorizeSubject(instance.Module.Digest)))

for _, image := range instance.Images {
log.Info(fmt.Sprintf("container image %s",
colorizeSubject(image)))
}

im := runtime.InstanceManager{Instance: apiv1.Instance{Inventory: instance.Inventory}}

objects, err := im.ListObjects()
if err != nil {
if apierrors.IsNotFound(err) {
log.Error(err, colorizeJoin(obj, errors.New("NotFound")))
return err
}

for _, obj := range objects {
err = rm.Client().Get(ctx, client.ObjectKeyFromObject(obj), obj)
if err != nil {
if apierrors.IsNotFound(err) {
log.Error(err, colorizeJoin(obj, errors.New("NotFound")))
failed = true

continue
}
log.Error(err, colorizeJoin(obj, errors.New("Unknown")))
failed = true
continue
}
log.Error(err, colorizeJoin(obj, errors.New("Unknown")))
continue
}

res, err := status.Compute(obj)
if err != nil {
log.Error(err, colorizeJoin(obj, errors.New("Failed")))
continue
res, err := status.Compute(obj)
if err != nil {
log.Error(err, colorizeJoin(obj, errors.New("Failed")))
failed = true
continue
}
log.Info(colorizeJoin(obj, res.Status, "-", res.Message))
}
log.Info(colorizeJoin(obj, res.Status, "-", res.Message))
}
}

if failed {
return fmt.Errorf("completed with errors")
}
return nil
}
76 changes: 75 additions & 1 deletion cmd/timoni/bundle_status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package main
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"testing"

Expand Down Expand Up @@ -108,7 +110,7 @@ bundle: {
g.Expect(err).ToNot(HaveOccurred())

output, err := executeCommand(fmt.Sprintf("bundle status %s", bundleName))
g.Expect(err).ToNot(HaveOccurred())
g.Expect(err).To(HaveOccurred())
g.Expect(output).To(ContainSubstring(fmt.Sprintf("ConfigMap/%s/frontend-client Current", namespace)))
g.Expect(output).To(ContainSubstring(fmt.Sprintf("ConfigMap/%s/backend-server NotFound", namespace)))
})
Expand Down Expand Up @@ -171,3 +173,75 @@ bundle: {
g.Expect(output).ToNot(ContainSubstring("timoni:latest-dev@sha"))
})
}

func Test_BundleStatus_Runtime(t *testing.T) {
g := NewWithT(t)

bundleName := rnd("my-bundle", 5)
modPath := "testdata/module"
namespace := rnd("my-namespace", 5)
modName := rnd("my-mod", 5)
modURL := fmt.Sprintf("%s/%s", dockerRegistry, modName)
modVer := "1.0.0"

_, err := executeCommand(fmt.Sprintf(
"mod push %s oci://%s -v %s",
modPath,
modURL,
modVer,
))
g.Expect(err).ToNot(HaveOccurred())

bundleData := fmt.Sprintf(`
bundle: {
_cluster: string @timoni(runtime:string:TIMONI_CLUSTER_NAME)
apiVersion: "v1alpha1"
name: "%[1]s"
instances: {
"\(_cluster)-app": {
module: {
url: "oci://%[2]s"
version: "%[3]s"
}
namespace: "%[4]s"
}
}
}
`, bundleName, modURL, modVer, namespace)

runtimeCue := `
runtime: {
apiVersion: "v1alpha1"
name: "fleet-test"
clusters: {
"staging": {
group: "staging"
kubeContext: "envtest"
}
"production": {
group: "production"
kubeContext: "envtest"
}
}
values: []
}
`

runtimePath := filepath.Join(t.TempDir(), "runtime.cue")
g.Expect(os.WriteFile(runtimePath, []byte(runtimeCue), 0644)).ToNot(HaveOccurred())

_, err = executeCommandWithIn(
fmt.Sprintf("bundle apply -f- -r %s -p main --wait", runtimePath),
strings.NewReader(bundleData))
g.Expect(err).ToNot(HaveOccurred())

t.Run("lists instances across clusters", func(t *testing.T) {
g := NewWithT(t)

output, err := executeCommand(fmt.Sprintf("bundle status %s -r %s", bundleName, runtimePath))
g.Expect(err).ToNot(HaveOccurred())
g.Expect(output).To(ContainSubstring("staging-app"))
g.Expect(output).To(ContainSubstring("production-app"))
})
}

0 comments on commit 330b84e

Please sign in to comment.