Skip to content

Commit

Permalink
Add multi-cluster support to bundle vet 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 0c9fc87 commit b4e28c1
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 38 deletions.
106 changes: 68 additions & 38 deletions cmd/timoni/bundle_vet.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"fmt"
"maps"
"os"
"path"

"cuelang.org/go/cue"
"cuelang.org/go/cue/cuecontext"
Expand Down Expand Up @@ -112,65 +113,94 @@ func runBundleVetCmd(cmd *cobra.Command, args []string) error {
maps.Copy(runtimeValues, engine.GetEnv())
}

if len(bundleArgs.runtimeFiles) > 0 {
kctx, cancel := context.WithTimeout(cmd.Context(), rootArgs.timeout)
defer cancel()
rt, err := buildRuntime(bundleArgs.runtimeFiles)
if err != nil {
return err
}

rt, err := buildRuntime(bundleArgs.runtimeFiles)
if err != nil {
return err
}
clusters := rt.SelectClusters(bundleArgs.runtimeCluster, bundleArgs.runtimeClusterGroup)
if len(clusters) == 0 {
return fmt.Errorf("no cluster found")
}

kctx, cancel := context.WithTimeout(cmd.Context(), rootArgs.timeout)
defer cancel()

for _, cluster := range clusters {
kubeconfigArgs.Context = &cluster.KubeContext

clusterValues := make(map[string]string)

// add values from env
maps.Copy(clusterValues, runtimeValues)

// add values from cluster
rm, err := runtime.NewResourceManager(kubeconfigArgs)
if err != nil {
return err
}

reader := runtime.NewResourceReader(rm)
rv, err := reader.Read(kctx, rt.Refs)
if err != nil {
return err
}
maps.Copy(clusterValues, rv)

maps.Copy(runtimeValues, rv)
}

if err := bm.InitWorkspace(tmpDir, runtimeValues); err != nil {
return describeErr(tmpDir, "failed to parse bundle", err)
}
// add cluster info
maps.Copy(clusterValues, cluster.NameGroupValues())

v, err := bm.Build()
if err != nil {
return describeErr(tmpDir, "failed to build bundle", err)
}
// create cluster workspace
workspace := path.Join(tmpDir, cluster.Name)
if err := os.MkdirAll(workspace, os.ModePerm); err != nil {
return err
}

bundle, err := bm.GetBundle(v)
if err != nil {
return err
}
log = LoggerBundle(logr.NewContext(cmd.Context(), log), bundle.Name, apiv1.RuntimeDefaultName)
if err := bm.InitWorkspace(workspace, clusterValues); err != nil {
return describeErr(workspace, "failed to parse bundle", err)
}

if len(bundle.Instances) == 0 {
return fmt.Errorf("no instances found in bundle")
}
v, err := bm.Build()
if err != nil {
return describeErr(workspace, "failed to build bundle", err)
}

if bundleVetArgs.printValue {
val := v.LookupPath(cue.ParsePath("bundle"))
if val.Err() != nil {
bundle, err := bm.GetBundle(v)
if err != nil {
return err
}
_, err := rootCmd.OutOrStdout().Write([]byte(fmt.Sprintf("bundle: %v\n", val)))
return err
}

for _, i := range bundle.Instances {
if i.Namespace == "" {
return fmt.Errorf("instance %s does not have a namespace", i.Name)
log = LoggerBundle(logr.NewContext(cmd.Context(), log), bundle.Name, apiv1.RuntimeDefaultName)

if len(bundle.Instances) == 0 {
return fmt.Errorf("no instances found in bundle")
}

if bundleVetArgs.printValue {
val := v.LookupPath(cue.ParsePath("bundle"))
if val.Err() != nil {
return err
}
bundleCue := fmt.Sprintf("bundle: %v\n", val)
if cluster.Name != apiv1.RuntimeDefaultName {
bundleCue = fmt.Sprintf("\"%s\": bundle: %v\n", cluster.Name, val)
}
_, err := rootCmd.OutOrStdout().Write([]byte(bundleCue))
if err != nil {
return err
}
} else {
for _, i := range bundle.Instances {
if i.Namespace == "" {
return fmt.Errorf("instance %s does not have a namespace", i.Name)
}
log := LoggerBundleInstance(logr.NewContext(cmd.Context(), log), bundle.Name, cluster.Name, i.Name)
log.Info("instance is valid")
}
}
log := LoggerBundleInstance(logr.NewContext(cmd.Context(), log), bundle.Name, apiv1.RuntimeDefaultName, i.Name)
log.Info("instance is valid")
}

log.Info("bundle is valid")
if !bundleVetArgs.printValue {
log.Info("bundle is valid")
}
return nil
}
114 changes: 114 additions & 0 deletions cmd/timoni/bundle_vet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,3 +292,117 @@ bundle:
g.Expect(err).ToNot(HaveOccurred())
g.Expect(output).To(BeEquivalentTo(bundleComputed))
}

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

bundleCue := `
bundle: {
_cluster: "dev" @timoni(runtime:string:TIMONI_CLUSTER_NAME)
_env: "dev" @timoni(runtime:string:TIMONI_CLUSTER_GROUP)
apiVersion: "v1alpha1"
name: "fleet-test"
instances: {
"frontend": {
module: {
url: "oci://ghcr.io/stefanprodan/timoni/minimal"
version: "latest"
}
namespace: "fleet-test"
values: {
message: "Hello from cluster \(_cluster)"
test: enabled: true
if _env == "staging" {
replicas: 2
}
if _env == "production" {
replicas: 3
}
}
}
}
}
`
runtimeCue := `
runtime: {
apiVersion: "v1alpha1"
name: "fleet-test"
clusters: {
"staging": {
group: "staging"
kubeContext: "envtest"
}
"production": {
group: "production"
kubeContext: "envtest"
}
}
values: [
{
query: "k8s:v1:Namespace:kube-system"
for: {
"CLUSTER_UID": "obj.metadata.uid"
}
},
]
}
`

bundleComputed := `"staging": bundle: {
apiVersion: "v1alpha1"
name: "fleet-test"
instances: {
frontend: {
module: {
url: "oci://ghcr.io/stefanprodan/timoni/minimal"
version: "latest"
}
namespace: "fleet-test"
values: {
message: "Hello from cluster staging"
replicas: 2
test: {
enabled: true
}
}
}
}
}
"production": bundle: {
apiVersion: "v1alpha1"
name: "fleet-test"
instances: {
frontend: {
module: {
url: "oci://ghcr.io/stefanprodan/timoni/minimal"
version: "latest"
}
namespace: "fleet-test"
values: {
message: "Hello from cluster production"
replicas: 3
test: {
enabled: true
}
}
}
}
}
`
wd := t.TempDir()
bundlePath := filepath.Join(wd, "bundle.cue")
g.Expect(os.WriteFile(bundlePath, []byte(bundleCue), 0644)).ToNot(HaveOccurred())

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

output, err := executeCommand(fmt.Sprintf(
"bundle vet -f %s -r %s -p main --print-value",
bundlePath, runtimePath,
))
g.Expect(err).ToNot(HaveOccurred())
g.Expect(output).To(BeEquivalentTo(bundleComputed))
}

0 comments on commit b4e28c1

Please sign in to comment.