Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor cmd/timoni and extract reconciler logic to dedicated package #366

Merged
merged 10 commits into from
Jun 11, 2024
179 changes: 29 additions & 150 deletions cmd/timoni/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,16 @@ import (
"fmt"
"os"
"strings"
"time"

"cuelang.org/go/cue/cuecontext"
"github.com/fluxcd/pkg/ssa"
"github.com/go-logr/logr"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

apiv1 "github.com/stefanprodan/timoni/api/v1alpha1"
"github.com/stefanprodan/timoni/internal/engine"
"github.com/stefanprodan/timoni/internal/engine/fetcher"
"github.com/stefanprodan/timoni/internal/flags"
"github.com/stefanprodan/timoni/internal/logger"
"github.com/stefanprodan/timoni/internal/reconciler"
"github.com/stefanprodan/timoni/internal/runtime"
)

Expand Down Expand Up @@ -142,7 +140,7 @@ func runApplyCmd(cmd *cobra.Command, args []string) error {
applyArgs.name = args[0]
applyArgs.module = args[1]

log := LoggerInstance(cmd.Context(), applyArgs.name)
log := loggerInstance(cmd.Context(), applyArgs.name, true)

version := applyArgs.version.String()
if version == "" {
Expand Down Expand Up @@ -224,155 +222,36 @@ func runApplyCmd(cmd *cobra.Command, args []string) error {
return describeErr(f.GetModuleRoot(), "build failed", err)
}

finalValues, err := builder.GetDefaultValues()
if err != nil {
return fmt.Errorf("failed to extract values: %w", err)
}

applySets, err := builder.GetApplySets(buildResult)
if err != nil {
return fmt.Errorf("failed to extract objects: %w", err)
}

var objects []*unstructured.Unstructured
for _, set := range applySets {
objects = append(objects, set.Objects...)
}

rm, err := runtime.NewResourceManager(kubeconfigArgs)
if err != nil {
return err
}

rm.SetOwnerLabels(objects, applyArgs.name, *kubeconfigArgs.Namespace)

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

exists := false
sm := runtime.NewStorageManager(rm)
instance, err := sm.Get(ctx, applyArgs.name, *kubeconfigArgs.Namespace)
if err == nil {
exists = true
}

nsExists, err := sm.NamespaceExists(ctx, *kubeconfigArgs.Namespace)
if err != nil {
return fmt.Errorf("instance init failed: %w", err)
}

if !applyArgs.overwriteOwnership && exists {
err = instanceOwnershipConflicts(*instance)
if err != nil {
return err
}
instance := &engine.BundleInstance{
Name: applyArgs.name,
Namespace: *kubeconfigArgs.Namespace,
Module: *mod,
Bundle: "",
}

im := runtime.NewInstanceManager(applyArgs.name, *kubeconfigArgs.Namespace, finalValues, *mod)

if err := im.AddObjects(objects); err != nil {
return fmt.Errorf("adding objects to instance failed: %w", err)
}

staleObjects, err := sm.GetStaleObjects(ctx, &im.Instance)
if err != nil {
return fmt.Errorf("getting stale objects failed: %w", err)
}

if applyArgs.dryrun || applyArgs.diff {
if !nsExists {
log.Info(colorizeJoin(colorizeNamespaceFromArgs(), ssa.CreatedAction, dryRunServer))
}
return instanceDryRunDiff(logr.NewContext(ctx, log), rm, objects, staleObjects, nsExists, tmpDir, applyArgs.diff)
}

if !exists {
log.Info(fmt.Sprintf("installing %s in namespace %s", applyArgs.name, *kubeconfigArgs.Namespace))

if err := sm.Apply(ctx, &im.Instance, true); err != nil {
return fmt.Errorf("instance init failed: %w", err)
}

if !nsExists {
log.Info(colorizeJoin(colorizeNamespaceFromArgs(), ssa.CreatedAction))
}
} else {
log.Info(fmt.Sprintf("upgrading %s in namespace %s", applyArgs.name, *kubeconfigArgs.Namespace))
}

applyOpts := runtime.ApplyOptions(applyArgs.force, rootArgs.timeout)
applyOpts.WaitInterval = 5 * time.Second

waitOptions := ssa.WaitOptions{
Interval: applyOpts.WaitInterval,
Timeout: rootArgs.timeout,
FailFast: true,
}

for _, set := range applySets {
if len(applySets) > 1 {
log.Info(fmt.Sprintf("applying %s", set.Name))
}

cs, err := rm.ApplyAllStaged(ctx, set.Objects, applyOpts)
if err != nil {
return err
}
for _, change := range cs.Entries {
log.Info(colorizeJoin(change))
}

if applyArgs.wait {
spin := StartSpinner(fmt.Sprintf("waiting for %v resource(s) to become ready...", len(set.Objects)))
err = rm.Wait(set.Objects, waitOptions)
spin.Stop()
if err != nil {
return err
}
log.Info("resources are ready")
}
}

if images, err := builder.GetContainerImages(buildResult); err == nil {
im.Instance.Images = images
}

if err := sm.Apply(ctx, &im.Instance, true); err != nil {
return fmt.Errorf("storing instance failed: %w", err)
}

var deletedObjects []*unstructured.Unstructured
if len(staleObjects) > 0 {
deleteOpts := runtime.DeleteOptions(applyArgs.name, *kubeconfigArgs.Namespace)
changeSet, err := rm.DeleteAll(ctx, staleObjects, deleteOpts)
if err != nil {
return fmt.Errorf("pruning objects failed: %w", err)
}
deletedObjects = runtime.SelectObjectsFromSet(changeSet, ssa.DeletedAction)
for _, change := range changeSet.Entries {
log.Info(colorizeJoin(change))
}
}

if applyArgs.wait {
if len(deletedObjects) > 0 {
spin := StartSpinner(fmt.Sprintf("waiting for %v resource(s) to be finalized...", len(deletedObjects)))
err = rm.WaitForTermination(deletedObjects, waitOptions)
spin.Stop()
if err != nil {
return fmt.Errorf("waiting for termination failed: %w", err)
}

log.Info("all resources are ready")
}
}

return nil
}

func instanceOwnershipConflicts(instance apiv1.Instance) error {
if currentOwnerBundle := instance.Labels[apiv1.BundleNameLabelKey]; currentOwnerBundle != "" {
return fmt.Errorf("instance ownership conflict encountered. Apply with \"--overwrite-ownership\" to gain instance ownership. Conflict: instance \"%s\" exists and is managed by bundle \"%s\"", instance.Name, currentOwnerBundle)
r := reconciler.NewInteractiveReconciler(log,
&reconciler.CommonOptions{
Dir: tmpDir,
Wait: applyArgs.wait,
Force: applyArgs.force,
OverwriteOwnership: applyArgs.overwriteOwnership,
},
&reconciler.InteractiveOptions{
DryRun: applyArgs.dryrun,
Diff: applyArgs.diff,
DiffOutput: cmd.OutOrStdout(),
ProgressStart: logger.StartSpinner,
},
rootArgs.timeout,
)
if err := r.Init(ctx, builder, buildResult, instance, kubeconfigArgs); err != nil {
return annotateInstanceOwnershipConflictErr(err)
}
return nil
return r.ApplyInstance(ctx, log,
builder,
buildResult,
)
}
2 changes: 1 addition & 1 deletion cmd/timoni/apply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ bundle: {
modPath,
))
g.Expect(err).To(HaveOccurred())
g.Expect(err.Error()).To(ContainSubstring(fmt.Sprintf("instance \"%s\" exists and is managed by bundle \"%s\"", instanceName, bundleName)))
g.Expect(err.Error()).To(ContainSubstring(fmt.Sprintf("instance \"%s\" exists and is managed by another bundle \"%s\"", instanceName, bundleName)))

output, err := executeCommand(fmt.Sprintf("ls -n %[1]s", namespace))
g.Expect(err).ToNot(HaveOccurred())
Expand Down
3 changes: 2 additions & 1 deletion cmd/timoni/artifact_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/spf13/cobra"

"github.com/stefanprodan/timoni/internal/flags"
"github.com/stefanprodan/timoni/internal/logger"
"github.com/stefanprodan/timoni/internal/oci"
)

Expand Down Expand Up @@ -64,7 +65,7 @@ func listArtifactCmdRun(cmd *cobra.Command, args []string) error {
}
ociURL := args[0]

spin := StartSpinner("fetching tags")
spin := logger.StartSpinner("fetching tags")
defer spin.Stop()

ctx, cancel := context.WithTimeout(cmd.Context(), rootArgs.timeout)
Expand Down
5 changes: 3 additions & 2 deletions cmd/timoni/artifact_pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/spf13/cobra"

"github.com/stefanprodan/timoni/internal/flags"
"github.com/stefanprodan/timoni/internal/logger"
"github.com/stefanprodan/timoni/internal/oci"
)

Expand Down Expand Up @@ -125,7 +126,7 @@ func pullArtifactCmdRun(cmd *cobra.Command, args []string) error {
}
}

spin := StartSpinner("pulling artifact")
spin := logger.StartSpinner("pulling artifact")
defer spin.Stop()

ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
Expand All @@ -138,7 +139,7 @@ func pullArtifactCmdRun(cmd *cobra.Command, args []string) error {
}

spin.Stop()
log.Info(fmt.Sprintf("extracted: %s", colorizeSubject(pullArtifactArgs.output)))
log.Info(fmt.Sprintf("extracted: %s", logger.ColorizeSubject(pullArtifactArgs.output)))

return nil
}
7 changes: 4 additions & 3 deletions cmd/timoni/artifact_push.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
apiv1 "github.com/stefanprodan/timoni/api/v1alpha1"
"github.com/stefanprodan/timoni/internal/engine"
"github.com/stefanprodan/timoni/internal/flags"
"github.com/stefanprodan/timoni/internal/logger"
"github.com/stefanprodan/timoni/internal/oci"
)

Expand Down Expand Up @@ -128,7 +129,7 @@ func pushArtifactCmdRun(cmd *cobra.Command, args []string) error {
}
oci.AppendGitMetadata(pushArtifactArgs.path, annotations)

spin := StartSpinner("pushing artifact")
spin := logger.StartSpinner("pushing artifact")
defer spin.Stop()

opts := oci.Options(ctx, pushArtifactArgs.creds.String(), rootArgs.registryInsecure)
Expand Down Expand Up @@ -164,8 +165,8 @@ func pushArtifactCmdRun(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
log.Info(fmt.Sprintf("artifact: %s", colorizeSubject(ociURL)))
log.Info(fmt.Sprintf("digest: %s", colorizeSubject(digest.DigestStr())))
log.Info(fmt.Sprintf("artifact: %s", logger.ColorizeSubject(ociURL)))
log.Info(fmt.Sprintf("digest: %s", logger.ColorizeSubject(digest.DigestStr())))

return nil
}
5 changes: 3 additions & 2 deletions cmd/timoni/artifact_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/spf13/cobra"

"github.com/stefanprodan/timoni/internal/flags"
"github.com/stefanprodan/timoni/internal/logger"
"github.com/stefanprodan/timoni/internal/oci"
)

Expand Down Expand Up @@ -62,7 +63,7 @@ func tagArtifactCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("at least one tag is required")
}

spin := StartSpinner("tagging artifact")
spin := logger.StartSpinner("tagging artifact")
defer spin.Stop()

log := LoggerFrom(cmd.Context())
Expand All @@ -86,7 +87,7 @@ func tagArtifactCmdRun(cmd *cobra.Command, args []string) error {
}

for _, tag := range tagArtifactArgs.tags {
log.Info(fmt.Sprintf("tagged: %s", colorizeSubject(fmt.Sprintf("%s:%s", baseURL, tag))))
log.Info(fmt.Sprintf("tagged: %s", logger.ColorizeSubject(fmt.Sprintf("%s:%s", baseURL, tag))))
}

return nil
Expand Down
Loading