Skip to content

Commit 6030cc6

Browse files
authored
Add clm cli (#157)
* add first version of cli (clm) * add workflows to publish clm
1 parent 943d51d commit 6030cc6

File tree

18 files changed

+1297
-8
lines changed

18 files changed

+1297
-8
lines changed

.github/workflows/publish-clm.yaml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Publish clm
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
defaults:
8+
run:
9+
shell: bash
10+
11+
jobs:
12+
deploy:
13+
runs-on: [ubuntu-24.04]
14+
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v4
18+
19+
- name: Setup go
20+
uses: actions/setup-go@v5
21+
with:
22+
go-version-file: 'go.mod'
23+
24+
- name: Build
25+
run: |
26+
mkdir -p bin
27+
for os in linux darwin; do
28+
for arch in amd64 arm64; do
29+
file=bin/clm-$os-$arch
30+
echo "Building $file ..."
31+
LDFLAGS=""
32+
LDFLAGS+=" -X \"github.com/sap/component-operator-runtime/internal/version.version=${{ github.event.release.tag_name }}\""
33+
LDFLAGS+=" -X \"github.com/sap/component-operator-runtime/internal/version.gitCommit=${{ github.sha }}\""
34+
LDFLAGS+=" -X \"github.com/sap/component-operator-runtime/internal/version.gitTreeState=clean\""
35+
GOOS=$os GOARCH=$arch go build -o $file -ldflags "$LDFLAGS" ./clm
36+
done
37+
done
38+
39+
- name: Upload
40+
run: |
41+
for os in linux darwin; do
42+
for arch in amd64 arm64; do
43+
upload_url="${{ github.event.release.upload_url }}"
44+
upload_url=${upload_url%%\{*\}}
45+
file=bin/clm-$os-$arch
46+
echo "Uploading $file to $upload_url ..."
47+
curl -sSf \
48+
-H "Accept: application/vnd.github+json" \
49+
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
50+
-H "Content-Type: $(file -b --mime-type $file)" \
51+
--data-binary @$file \
52+
"$upload_url?name=$(basename $file)"
53+
done
54+
done

.github/workflows/publish-scaffold.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Publish scaffolder
1+
name: Publish scaffold
22

33
on:
44
release:
@@ -29,9 +29,9 @@ jobs:
2929
file=bin/scaffold-$os-$arch
3030
echo "Building $file ..."
3131
LDFLAGS=""
32-
LDFLAGS+=" -X github.com/sap/component-operator-runtime/internal/version.version=${{ github.event.release.tag_name }}"
33-
LDFLAGS+=" -X github.com/sap/component-operator-runtime/internal/version.gitCommit=${{ github.sha }}"
34-
LDFLAGS+=" -X github.com/sap/component-operator-runtime/internal/version.gitTreeState=clean"
32+
LDFLAGS+=" -X \"github.com/sap/component-operator-runtime/internal/version.version=${{ github.event.release.tag_name }}\""
33+
LDFLAGS+=" -X \"github.com/sap/component-operator-runtime/internal/version.gitCommit=${{ github.sha }}\""
34+
LDFLAGS+=" -X \"github.com/sap/component-operator-runtime/internal/version.gitTreeState=clean\""
3535
GOOS=$os GOARCH=$arch go build -o $file -ldflags "$LDFLAGS" ./scaffold
3636
done
3737
done

.github/workflows/run-tests.yaml

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,11 @@ jobs:
7474

7575
- name: Build scaffold
7676
run: |
77-
go build -o bin/scaffold -ldflags "-X \"main.version=$(pwd)\"" ./scaffold
77+
LDFLAGS=""
78+
LDFLAGS+=" -X \"github.com/sap/component-operator-runtime/internal/version.version=$(pwd)\""
79+
LDFLAGS+=" -X \"github.com/sap/component-operator-runtime/internal/version.gitCommit=${{ github.sha }}\""
80+
LDFLAGS+=" -X \"github.com/sap/component-operator-runtime/internal/version.gitTreeState=clean\""
81+
go build -o bin/scaffold -ldflags "$LDFLAGS" ./scaffold
7882
echo "Running 'scaffold --version' ..."
7983
bin/scaffold --version
8084
@@ -141,3 +145,28 @@ jobs:
141145
# run: |
142146
# cd $RUNNER_TEMP/src
143147
# make docker-build
148+
149+
test-clm:
150+
runs-on: ubuntu-24.04
151+
needs:
152+
- check-generate
153+
- unit-and-integration-tests
154+
155+
steps:
156+
- name: Checkout
157+
uses: actions/checkout@v4
158+
159+
- name: Setup go
160+
uses: actions/setup-go@v5
161+
with:
162+
go-version-file: 'go.mod'
163+
164+
- name: Build clm
165+
run: |
166+
LDFLAGS=""
167+
LDFLAGS+=" -X \"github.com/sap/component-operator-runtime/internal/version.version=$(pwd)\""
168+
LDFLAGS+=" -X \"github.com/sap/component-operator-runtime/internal/version.gitCommit=${{ github.sha }}\""
169+
LDFLAGS+=" -X \"github.com/sap/component-operator-runtime/internal/version.gitTreeState=clean\""
170+
go build -o bin/clm -ldflags "$LDFLAGS" ./clm
171+
echo "Running 'clm version -o json' ..."
172+
bin/clm version -o json

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ __debug_bin
2424
*.swo
2525
*~
2626
.DS_Store
27+
.vscode
2728

2829
# temp stuff
2930
/tmp

clm/clm.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and component-operator-runtime contributors
3+
SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package main
7+
8+
import (
9+
"os"
10+
11+
"github.com/go-logr/logr"
12+
13+
"sigs.k8s.io/controller-runtime/pkg/log"
14+
15+
"github.com/sap/component-operator-runtime/clm/cmd"
16+
)
17+
18+
func main() {
19+
// ctx, _ := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
20+
21+
log.SetLogger(logr.Discard())
22+
23+
if err := cmd.Execute(); err != nil {
24+
os.Exit(1)
25+
}
26+
}

clm/cmd/apply.go

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and component-operator-runtime contributors
3+
SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package cmd
7+
8+
import (
9+
"context"
10+
"fmt"
11+
"time"
12+
13+
"github.com/spf13/cobra"
14+
15+
apierrors "k8s.io/apimachinery/pkg/api/errors"
16+
utilerrors "k8s.io/apimachinery/pkg/util/errors"
17+
18+
"github.com/sap/component-operator-runtime/clm/internal/backoff"
19+
"github.com/sap/component-operator-runtime/clm/internal/manifests"
20+
"github.com/sap/component-operator-runtime/clm/internal/release"
21+
"github.com/sap/component-operator-runtime/pkg/component"
22+
"github.com/sap/component-operator-runtime/pkg/reconciler"
23+
"github.com/sap/go-generics/slices"
24+
)
25+
26+
const applyUsage = `Apply component manifests to Kubernetes cluster`
27+
28+
type applyOptions struct {
29+
valuesSources []string
30+
timeout time.Duration
31+
}
32+
33+
func newApplyCmd() *cobra.Command {
34+
options := &applyOptions{}
35+
36+
cmd := &cobra.Command{
37+
Use: "apply NAME SOURCE...",
38+
Short: "Apply component",
39+
Long: applyUsage,
40+
SilenceUsage: true,
41+
Args: cobra.MinimumNArgs(2),
42+
PreRunE: func(c *cobra.Command, args []string) error {
43+
return nil
44+
},
45+
RunE: func(c *cobra.Command, args []string) (err error) {
46+
name := args[0]
47+
manifestSources := args[1:]
48+
namespace := c.Flag("namespace").Value.String()
49+
50+
clnt, err := getClient(c.Flag("kubeconfig").Value.String())
51+
if err != nil {
52+
return err
53+
}
54+
55+
reconciler := reconciler.NewReconciler(fullName, clnt, reconciler.ReconcilerOptions{
56+
UpdatePolicy: ref(reconciler.UpdatePolicySsaOverride),
57+
})
58+
59+
releaseClient := release.NewClient(fullName, clnt)
60+
61+
ownerId := fullName + "/" + namespace + "/" + name
62+
63+
objects, err := manifests.Generate(manifestSources, options.valuesSources, fullName, clnt, namespace, name)
64+
if err != nil {
65+
return err
66+
}
67+
68+
release, err := releaseClient.Get(context.TODO(), namespace, name)
69+
if err != nil {
70+
if apierrors.IsNotFound(err) {
71+
release, err = releaseClient.Create(context.TODO(), namespace, name)
72+
if err != nil {
73+
return err
74+
}
75+
} else {
76+
return err
77+
}
78+
}
79+
80+
if release.IsDeleting() {
81+
return fmt.Errorf("release %s/%s is being deleted; updates are not allowed in this state", release.GetNamespace(), release.GetName())
82+
}
83+
84+
release.Revision += 1
85+
86+
backoff := backoff.New()
87+
88+
var timeout <-chan time.Time
89+
if options.timeout > 0 {
90+
timeout = time.After(options.timeout)
91+
}
92+
93+
defer func() {
94+
if err != nil {
95+
release.State = component.StateError
96+
}
97+
if updateErr := releaseClient.Update(context.TODO(), release); updateErr != nil {
98+
err = utilerrors.NewAggregate([]error{err, updateErr})
99+
}
100+
}()
101+
102+
for {
103+
release.State = component.StateProcessing
104+
ok, err := reconciler.Apply(context.TODO(), &release.Inventory, objects, namespace, ownerId, release.Revision)
105+
if err != nil {
106+
return err
107+
}
108+
if ok {
109+
release.State = component.StateReady
110+
break
111+
}
112+
if err := releaseClient.Update(context.TODO(), release); err != nil {
113+
return err
114+
}
115+
select {
116+
case <-time.After(backoff.Next()):
117+
case <-timeout:
118+
return fmt.Errorf("timeout applying release %s/%s", release.GetNamespace(), release.GetName())
119+
}
120+
}
121+
122+
fmt.Printf("Release %s/%s successfully applied\n", release.GetNamespace(), release.GetName())
123+
124+
return nil
125+
},
126+
ValidArgsFunction: func(c *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
127+
if len(args) > 0 {
128+
return nil, cobra.ShellCompDirectiveDefault
129+
}
130+
if clnt, err := getClient(c.Flag("kubeconfig").Value.String()); err == nil {
131+
releaseClient := release.NewClient(fullName, clnt)
132+
namespace := c.Flag("namespace").Value.String()
133+
if namespace == "" {
134+
namespace = "default"
135+
}
136+
ctx, cancel := context.WithTimeout(context.TODO(), 3*time.Second)
137+
defer cancel()
138+
if releases, err := releaseClient.List(ctx, namespace); err == nil {
139+
return slices.Collect(releases, func(release *release.Release) string { return release.GetName() }), cobra.ShellCompDirectiveNoFileComp
140+
}
141+
}
142+
return nil, cobra.ShellCompDirectiveDefault
143+
},
144+
}
145+
146+
flags := cmd.Flags()
147+
flags.StringArrayVarP(&options.valuesSources, "values", "f", nil, "Path to values file in yaml format (can be repeated, values will be merged in order of appearance)")
148+
flags.DurationVar(&options.timeout, "timeout", 0, "Time to wait for the operation to complete (default is to wait forever)")
149+
150+
return cmd
151+
}

0 commit comments

Comments
 (0)