Skip to content

Commit

Permalink
Merge pull request #19 from stefanprodan/latest-version
Browse files Browse the repository at this point in the history
Introduce the concept of latest stable version for modules
  • Loading branch information
stefanprodan authored Feb 25, 2023
2 parents 4eeda97 + 9df741e commit 6a2e077
Show file tree
Hide file tree
Showing 16 changed files with 205 additions and 91 deletions.
11 changes: 4 additions & 7 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,14 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Restore Go cache
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: 1.20.x
cache: true
cache-dependency-path: |
**/go.sum
**/go.mod
- name: Setup CUE
uses: cue-lang/setup-cue@main
with:
Expand Down
11 changes: 4 additions & 7 deletions .github/workflows/docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,14 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Restore Go cache
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: 1.20.x
cache: true
cache-dependency-path: |
**/go.sum
**/go.mod
- name: Generate cmd docs
run: make docs
- name: Run mkdocs
Expand Down
26 changes: 12 additions & 14 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,14 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Restore Go cache
uses: actions/cache@v1
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: 1.20.x
cache: true
cache-dependency-path: |
**/go.sum
**/go.mod
- name: Setup Kubernetes
uses: helm/[email protected]
with:
Expand All @@ -42,7 +39,8 @@ jobs:
- name: Push
run: |
timoni mod push ./examples/podinfo oci://localhost:5000/podinfo \
--version 1.0.0
--version 1.0.0 \
--latest
- name: Template
run: |
timoni template podinfo oci://localhost:5000/podinfo \
Expand All @@ -51,23 +49,20 @@ jobs:
- name: Install
run: |
timoni install podinfo oci://localhost:5000/podinfo \
-v 1.0.0 \
-n test \
--namespace test \
--wait
- name: Upgrade (enable HPA)
run: |
timoni upgrade podinfo oci://localhost:5000/podinfo \
-f ./examples/podinfo-values/ha-values.cue \
-v 1.0.0 \
-n test \
--namespace test \
--wait
- name: Upgrade (disable HPA, enable ingress)
run: |
timoni apply podinfo oci://localhost:5000/podinfo \
-f ./examples/podinfo-values/psp-values.cue \
-f ./examples/podinfo-values/ingress-values.cue \
-v 1.0.0 \
-n test \
--namespace test \
--wait
- name: List
run: |
Expand All @@ -77,6 +72,9 @@ jobs:
timoni inspect resources podinfo -n test
timoni inspect module podinfo -n test
timoni inspect values podinfo -n test
- name: Status
run: |
timoni status podinfo -n test
- name: Uninstall
run: |
timoni uninstall podinfo -n test --wait
4 changes: 4 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ jobs:
uses: actions/setup-go@v3
with:
go-version: 1.20.x
cache: true
cache-dependency-path: |
**/go.sum
**/go.mod
- name: Setup Syft
uses: anchore/sbom-action/download-syft@v0
- name: Run GoReleaser
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ lint-samples: build
./bin/timoni mod lint ./internal/engine/testdata/module
cue fmt ./internal/engine/testdata/module-values

PODINFO_VER=$(shell cat ./examples/podinfo/templates/config.cue | awk '/tag:/ {print $$2}' | tr -d '*"')
push-podinfo: build
$pv=$(shell cat ./examples/podinfo/templates/config.cue | awk '/tag:/ {print $$2}' | tr -d '*"')
./bin/timoni push ./examples/podinfo oci://ghcr.io/stefanprodan/modules/podinfo --version $$pv --source https://github.com/stefanprodan/podinfo
./bin/timoni mod push ./examples/podinfo oci://ghcr.io/stefanprodan/modules/podinfo -v $(PODINFO_VER) --latest --source https://github.com/stefanprodan/podinfo

.PHONY: install
install: ## Build and install the CLI binary.
Expand Down
52 changes: 29 additions & 23 deletions cmd/timoni/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,13 @@ func runApplyCmd(cmd *cobra.Command, args []string) error {
applyArgs.name = args[0]
applyArgs.module = args[1]

version := applyArgs.version.String()
if version == "" {
version = engine.LatestTag
}

if strings.HasPrefix(applyArgs.module, oci.OCIRepositoryPrefix) {
logger.Printf("pulling %s:%s", applyArgs.module, applyArgs.version.String())
logger.Printf("pulling %s:%s", applyArgs.module, version)
} else {
logger.Println("building", applyArgs.module)
}
Expand All @@ -141,7 +146,7 @@ func runApplyCmd(cmd *cobra.Command, args []string) error {
fetcher := engine.NewFetcher(
ctxPull,
applyArgs.module,
applyArgs.version.String(),
version,
tmpDir,
applyArgs.creds.String(),
)
Expand All @@ -164,6 +169,8 @@ func runApplyCmd(cmd *cobra.Command, args []string) error {
return err
}

logger.Printf("using module %s version %s", mod.Name, mod.Version)

if len(applyArgs.valuesFiles) > 0 {
err = builder.MergeValuesFile(applyArgs.valuesFiles)
if err != nil {
Expand All @@ -186,28 +193,27 @@ func runApplyCmd(cmd *cobra.Command, args []string) error {
return fmt.Errorf("failed to extract objects, error: %w", err)
}

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

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

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

iExists := false
iName := fmt.Sprintf("%s/%s", applyArgs.name, *kubeconfigArgs.Namespace)
iStorage := runtime.NewStorageManager(sm)
if _, err := iStorage.Get(ctx, applyArgs.name, *kubeconfigArgs.Namespace); err == nil {
iExists = true
exists := false
sm := runtime.NewStorageManager(rm)
if _, err := sm.Get(ctx, applyArgs.name, *kubeconfigArgs.Namespace); err == nil {
exists = true
}

if applyArgs.dryrun || applyArgs.diff {
diffOpts := ssa.DefaultDiffOptions()
sort.Sort(ssa.SortableUnstructureds(objects))
for _, r := range objects {
change, liveObject, mergedObject, err := sm.Diff(ctx, r, diffOpts)
change, liveObject, mergedObject, err := rm.Diff(ctx, r, diffOpts)
if err != nil {
logger.Println(err)
continue
Expand Down Expand Up @@ -239,53 +245,53 @@ func runApplyCmd(cmd *cobra.Command, args []string) error {
return nil
}

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

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

if !iExists {
logger.Println("installing", iName)
if !exists {
logger.Printf("installing %s in namespace %s", applyArgs.name, *kubeconfigArgs.Namespace)

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

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

if !nsExists {
logger.Printf("Namespace/%s created", *kubeconfigArgs.Namespace)
}
} else {
logger.Println("upgrading", iName)
logger.Printf("upgrading %s in namespace %s", applyArgs.name, *kubeconfigArgs.Namespace)
}

applyOpts := runtime.ApplyOptions(applyArgs.force, time.Minute)
cs, err := sm.ApplyAllStaged(ctx, objects, applyOpts)
cs, err := rm.ApplyAllStaged(ctx, objects, applyOpts)
if err != nil {
return err
}
for _, change := range cs.Entries {
logger.Println(change.String())
}

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

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

var deletedObjects []*unstructured.Unstructured
if len(staleObjects) > 0 {
deleteOpts := runtime.DeleteOptions(applyArgs.name, *kubeconfigArgs.Namespace)
changeSet, err := sm.DeleteAll(ctx, staleObjects, deleteOpts)
changeSet, err := rm.DeleteAll(ctx, staleObjects, deleteOpts)
if err != nil {
return fmt.Errorf("prunning objects failed, error: %w", err)
}
Expand All @@ -297,14 +303,14 @@ func runApplyCmd(cmd *cobra.Command, args []string) error {

if applyArgs.wait {
logger.Println(fmt.Sprintf("waiting for %v resource(s) to become ready...", len(objects)))
err = sm.Wait(objects, ssa.DefaultWaitOptions())
err = rm.Wait(objects, ssa.DefaultWaitOptions())
if err != nil {
return err
}

if len(deletedObjects) > 0 {
logger.Printf("waiting for %v resource(s) to be finalized...", len(deletedObjects))
err = sm.WaitForTermination(deletedObjects, ssa.DefaultWaitOptions())
err = rm.WaitForTermination(deletedObjects, ssa.DefaultWaitOptions())
if err != nil {
return fmt.Errorf("wating for termination failed, error: %w", err)
}
Expand Down
7 changes: 6 additions & 1 deletion cmd/timoni/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ func runBuildCmd(cmd *cobra.Command, args []string) error {
buildArgs.name = args[0]
buildArgs.module = args[1]

version := buildArgs.version.String()
if version == "" {
version = engine.LatestTag
}

ctx := cuecontext.New()

tmpDir, err := os.MkdirTemp("", apiv1.FieldManager)
Expand All @@ -94,7 +99,7 @@ func runBuildCmd(cmd *cobra.Command, args []string) error {
fetcher := engine.NewFetcher(
ctxPull,
buildArgs.module,
buildArgs.version.String(),
version,
tmpDir,
buildArgs.creds.String(),
)
Expand Down
40 changes: 40 additions & 0 deletions cmd/timoni/inspect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,43 @@ func TestInspect(t *testing.T) {
g.Expect(output).To(ContainSubstring(fmt.Sprintf("ConfigMap/%s/%s-client", namespace, name)))
})
}

func TestInspect_Latest(t *testing.T) {
g := NewWithT(t)
modPath := "testdata/module"
modURL := fmt.Sprintf("oci://%s/%s", dockerRegistry, rnd("my-mod", 5))
modVer := "1.0.0"
name := rnd("my-instance", 5)
namespace := rnd("my-namespace", 5)

// Package the module as an OCI artifact and push it to registry
_, err := executeCommand(fmt.Sprintf(
"mod push %s %s -v %s --latest",
modPath,
modURL,
modVer,
))
g.Expect(err).ToNot(HaveOccurred())

// Install the latest version from the registry
_, err = executeCommand(fmt.Sprintf(
"apply -n %s %s %s -p main --wait",
namespace,
name,
modURL,
))
g.Expect(err).ToNot(HaveOccurred())

t.Run("inspect module", func(t *testing.T) {
g := NewWithT(t)
output, err := executeCommand(fmt.Sprintf(
"inspect module -n %s %s",
namespace,
name,
))
g.Expect(err).ToNot(HaveOccurred())

// Verify inspect output contains the module semver
g.Expect(output).To(ContainSubstring(modVer))
})
}
15 changes: 12 additions & 3 deletions cmd/timoni/mod_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ func init() {
}

const (
modTemplateURL = "ghcr.io/stefanprodan/modules/podinfo:6.3.4"
modTemplateName = "podinfo"
modTemplateName = "podinfo"
modTemplateURL = "ghcr.io/stefanprodan/modules/podinfo"
modTemplateImageRepo = "ghcr.io/stefanprodan"
)

func runInitModCmd(cmd *cobra.Command, args []string) error {
Expand Down Expand Up @@ -107,7 +108,7 @@ func copyModuleFile(mName, mTmpl, src, dst string) (err error) {
}
}()

if filepath.Base(in.Name()) == "values.cue" {
if filepath.Base(in.Name()) == "README.md" {
_, err = io.Copy(out, in)
if err != nil {
return
Expand All @@ -118,6 +119,14 @@ func copyModuleFile(mName, mTmpl, src, dst string) (err error) {
return err
}
txt := strings.Replace(string(data), mTmpl, mName, -1)

// TODO: find a better way to preserve the container image original name
txt = strings.Replace(
txt,
fmt.Sprintf("%s/%s", modTemplateImageRepo, mName),
fmt.Sprintf("%s/%s", modTemplateImageRepo, mTmpl),
-1,
)
_, err = io.WriteString(out, txt)
if err != nil {
return err
Expand Down
Loading

0 comments on commit 6a2e077

Please sign in to comment.