Skip to content

Commit 4c147c7

Browse files
authored
[Feature] [License] Activation CLI (#1972)
1 parent 3c34c2e commit 4c147c7

25 files changed

+1400
-102
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A)
44
- (Bugfix) (Platform) Increase memory limit for Inventory
55
- (Feature) (LM) Inventory Generator
6+
- (Feature) (License) Activation CLI
67

78
## [1.3.1](https://github.com/arangodb/kube-arangodb/tree/1.3.1) (2025-10-07)
89
- (Documentation) Add ArangoPlatformStorage Docs & Examples

docs/cli/arangodb_operator_platform.md

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Usage:
1414
Available Commands:
1515
completion Generate the autocompletion script for the specified shell
1616
help Help about any command
17-
license License Package related operations
17+
license License related Operations
1818
package Release Package related operations
1919
2020
Flags:
@@ -41,6 +41,7 @@ Available Commands:
4141
import Imports the package from the ZIP format
4242
install Installs the specified setup of the platform
4343
merge Merges definitions into single file
44+
registry Points all images to the new registry
4445
4546
Flags:
4647
-h, --help help for package
@@ -96,13 +97,16 @@ Global Flags:
9697

9798
[START_INJECT]: # (arangodb_operator_platform_license_cmd)
9899
```
99-
License Package related operations
100+
License related Operations
100101
101102
Usage:
102103
arangodb_operator_platform license [command]
103104
104105
Available Commands:
106+
activate Activates the License on ArangoDB Endpoint
107+
generate Generate the License
105108
inventory Inventory Generator
109+
secret Creates Platform Secret with Registry credentials
106110
107111
Flags:
108112
-h, --help help for license
@@ -138,3 +142,79 @@ Global Flags:
138142
-n, --namespace string Kubernetes Namespace (default "default")
139143
```
140144
[END_INJECT]: # (arangodb_operator_platform_license_inventory_cmd)
145+
146+
# ArangoDB Operator Platform License Activate Command
147+
148+
[START_INJECT]: # (arangodb_operator_platform_license_activate_cmd)
149+
```
150+
Activates the License on ArangoDB Endpoint
151+
152+
Usage:
153+
arangodb_operator_platform license activate [flags]
154+
155+
Flags:
156+
--arango.authentication string Arango Endpoint Auth Method. One of: Disabled, Basic, Token (default "Disabled")
157+
--arango.basic.password string Arango Password for Basic Authentication
158+
--arango.basic.username string Arango Username for Basic Authentication
159+
--arango.endpoint strings Arango Endpoint
160+
--arango.insecure Arango Endpoint Insecure
161+
--arango.token string Arango JWT Token for Authentication
162+
-h, --help help for activate
163+
--license.client.id string LicenseManager Client ID
164+
--license.client.secret string LicenseManager Client Secret
165+
--license.client.stage strings LicenseManager Stages (default [prd])
166+
--license.endpoint string LicenseManager Endpoint (default "license.arango.ai")
167+
--license.interval duration Interval of the license synchronization
168+
169+
Global Flags:
170+
--kubeconfig string Kubernetes Config File
171+
-n, --namespace string Kubernetes Namespace (default "default")
172+
```
173+
[END_INJECT]: # (arangodb_operator_platform_license_activate_cmd)
174+
175+
# ArangoDB Operator Platform License Generate Command
176+
177+
[START_INJECT]: # (arangodb_operator_platform_license_generate_cmd)
178+
```
179+
Generate the License
180+
181+
Usage:
182+
arangodb_operator_platform license generate [flags]
183+
184+
Flags:
185+
--deployment.id string Deployment ID
186+
-h, --help help for generate
187+
--inventory string Path to the Inventory File
188+
--license.client.id string LicenseManager Client ID
189+
--license.client.secret string LicenseManager Client Secret
190+
--license.client.stage strings LicenseManager Stages (default [prd])
191+
--license.endpoint string LicenseManager Endpoint (default "license.arango.ai")
192+
193+
Global Flags:
194+
--kubeconfig string Kubernetes Config File
195+
-n, --namespace string Kubernetes Namespace (default "default")
196+
```
197+
[END_INJECT]: # (arangodb_operator_platform_license_generate_cmd)
198+
199+
# ArangoDB Operator Platform License Secret Command
200+
201+
[START_INJECT]: # (arangodb_operator_platform_license_secret_cmd)
202+
```
203+
Creates Platform Secret with Registry credentials
204+
205+
Usage:
206+
arangodb_operator_platform license secret [flags]
207+
208+
Flags:
209+
-h, --help help for secret
210+
--license.client.id string LicenseManager Client ID
211+
--license.client.secret string LicenseManager Client Secret
212+
--license.client.stage strings LicenseManager Stages (default [prd])
213+
--license.endpoint string LicenseManager Endpoint (default "license.arango.ai")
214+
--secret string Kubernetes Secret Name
215+
216+
Global Flags:
217+
--kubeconfig string Kubernetes Config File
218+
-n, --namespace string Kubernetes Namespace (default "default")
219+
```
220+
[END_INJECT]: # (arangodb_operator_platform_license_secret_cmd)

internal/docs_test.go

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ func (d DocDefinitions) RenderMarkdown(t *testing.T, repositoryPath string) []by
7878

7979
els += 1
8080

81-
write(t, out, "### %s\n\n", el.Path)
82-
write(t, out, "Type: `%s` <sup>[\\[ref\\]](%s/%s#L%d)</sup>\n\n", el.Type, repositoryPath, el.File, el.Line)
81+
writef(t, out, "### %s\n\n", el.Path)
82+
writef(t, out, "Type: `%s` <sup>[\\[ref\\]](%s/%s#L%d)</sup>\n\n", el.Type, repositoryPath, el.File, el.Line)
8383

8484
if grade := el.Grade; grade != nil {
8585
switch grade.Grade {
@@ -88,44 +88,44 @@ func (d DocDefinitions) RenderMarkdown(t *testing.T, repositoryPath string) []by
8888
write(t, out, "> ***DEPRECATED***\n")
8989
write(t, out, "> \n")
9090
for _, line := range grade.Message {
91-
write(t, out, "> **%s**\n", line)
91+
writef(t, out, "> **%s**\n", line)
9292
}
9393
write(t, out, "\n")
9494
case DocDefinitionGradeAlpha:
9595
write(t, out, "> [!WARNING]\n")
9696
write(t, out, "> ***ALPHA***\n")
9797
write(t, out, "> \n")
9898
for _, line := range grade.Message {
99-
write(t, out, "> **%s**\n", line)
99+
writef(t, out, "> **%s**\n", line)
100100
}
101101
write(t, out, "\n")
102102
case DocDefinitionGradeBeta:
103103
write(t, out, "> [!IMPORTANT]\n")
104104
write(t, out, "> ***BETA***\n")
105105
write(t, out, "> \n")
106106
for _, line := range grade.Message {
107-
write(t, out, "> **%s**\n", line)
107+
writef(t, out, "> **%s**\n", line)
108108
}
109109
write(t, out, "\n")
110110
}
111111
}
112112

113113
if d := el.Important; d != nil {
114114
write(t, out, "> [!IMPORTANT]\n")
115-
write(t, out, "> **%s**\n\n", *d)
115+
writef(t, out, "> **%s**\n\n", *d)
116116
}
117117

118118
if d := el.Required; d != nil {
119119
if *d == "" {
120120
write(t, out, "This field is **required**\n\n")
121121
} else {
122-
write(t, out, "This field is **required**: %s\n\n", *d)
122+
writef(t, out, "This field is **required**: %s\n\n", *d)
123123
}
124124
}
125125

126126
if len(el.Docs) > 0 {
127127
for _, doc := range el.Docs {
128-
write(t, out, "%s\n", doc)
128+
writef(t, out, "%s\n", doc)
129129
}
130130
write(t, out, "\n")
131131
}
@@ -136,9 +136,9 @@ func (d DocDefinitions) RenderMarkdown(t *testing.T, repositoryPath string) []by
136136
for _, link := range el.Links {
137137
z := goStrings.Split(link, "|")
138138
if len(z) == 1 {
139-
write(t, out, "* [Documentation](%s)\n", z[0])
139+
writef(t, out, "* [Documentation](%s)\n", z[0])
140140
} else if len(z) == 2 {
141-
write(t, out, "* [%s](%s)\n", z[0], z[1])
141+
writef(t, out, "* [%s](%s)\n", z[0], z[1])
142142
} else {
143143
require.Fail(t, "Invalid link format")
144144
}
@@ -151,7 +151,7 @@ func (d DocDefinitions) RenderMarkdown(t *testing.T, repositoryPath string) []by
151151
write(t, out, "Example:\n")
152152
write(t, out, "```yaml\n")
153153
for _, example := range el.Example {
154-
write(t, out, "%s\n", example)
154+
writef(t, out, "%s\n", example)
155155
}
156156
write(t, out, "```\n\n")
157157
}
@@ -167,25 +167,25 @@ func (d DocDefinitions) RenderMarkdown(t *testing.T, repositoryPath string) []by
167167
}
168168

169169
if len(z) == 1 {
170-
write(t, out, "* %s\n", snip)
170+
writef(t, out, "* %s\n", snip)
171171
} else if len(z) == 2 {
172-
write(t, out, "* %s - %s\n", snip, z[1])
172+
writef(t, out, "* %s - %s\n", snip, z[1])
173173
} else {
174174
require.Fail(t, "Invalid enum format")
175175
}
176176
}
177177
write(t, out, "\n")
178178
} else {
179179
if d := el.Default; d != nil {
180-
write(t, out, "Default Value: `%s`\n\n", *d)
180+
writef(t, out, "Default Value: `%s`\n\n", *d)
181181
}
182182
}
183183

184184
if d := el.Immutable; d != nil {
185185
if *d == "" {
186186
write(t, out, "This field is **immutable**\n\n")
187187
} else {
188-
write(t, out, "This field is **immutable**: %s\n\n", *d)
188+
writef(t, out, "This field is **immutable**: %s\n\n", *d)
189189
}
190190
}
191191
}
@@ -545,10 +545,10 @@ func generateDocs(t *testing.T, objects map[string]map[string]interface{}, field
545545
"title": objName,
546546
"parent": apiIndexPageTitle,
547547
})
548-
write(t, out, "# API Reference for %s\n\n", objName)
548+
writef(t, out, "# API Reference for %s\n\n", objName)
549549

550550
util.IterateSorted(renderSections, func(name string, section []byte) {
551-
write(t, out, "## %s\n\n", util.BoolSwitch(name == "", "Object", name))
551+
writef(t, out, "## %s\n\n", util.BoolSwitch(name == "", "Object", name))
552552

553553
_, err = out.Write(section)
554554
require.NoError(t, err)
@@ -558,7 +558,12 @@ func generateDocs(t *testing.T, objects map[string]map[string]interface{}, field
558558
return outPaths
559559
}
560560

561-
func write(t *testing.T, out io.Writer, format string, args ...interface{}) {
561+
func write(t *testing.T, out io.Writer, format string) {
562+
_, err := out.Write([]byte(format))
563+
require.NoError(t, err)
564+
}
565+
566+
func writef(t *testing.T, out io.Writer, format string, args ...interface{}) {
562567
_, err := out.Write([]byte(fmt.Sprintf(format, args...)))
563568
require.NoError(t, err)
564569
}

internal/readme_cli.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,30 @@ func GenerateCLIArangoDBOperatorPlatformReadme(root string) error {
121121
readmeSections["arangodb_operator_platform_cmd"] = section
122122
}
123123

124+
if section, err := GenerateHelpQuoted(cmd, "license"); err != nil {
125+
return err
126+
} else {
127+
readmeSections["arangodb_operator_platform_license_cmd"] = section
128+
}
129+
130+
if section, err := GenerateHelpQuoted(cmd, "license", "activate"); err != nil {
131+
return err
132+
} else {
133+
readmeSections["arangodb_operator_platform_license_activate_cmd"] = section
134+
}
135+
136+
if section, err := GenerateHelpQuoted(cmd, "license", "generate"); err != nil {
137+
return err
138+
} else {
139+
readmeSections["arangodb_operator_platform_license_generate_cmd"] = section
140+
}
141+
142+
if section, err := GenerateHelpQuoted(cmd, "license", "secret"); err != nil {
143+
return err
144+
} else {
145+
readmeSections["arangodb_operator_platform_license_secret_cmd"] = section
146+
}
147+
124148
if section, err := GenerateHelpQuoted(cmd, "package"); err != nil {
125149
return err
126150
} else {

pkg/license/manager/client.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2025 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package manager
22+
23+
import (
24+
"context"
25+
"fmt"
26+
goHttp "net/http"
27+
"time"
28+
29+
"github.com/arangodb/go-driver"
30+
"github.com/arangodb/go-driver/http"
31+
32+
"github.com/arangodb/kube-arangodb/pkg/platform/inventory"
33+
"github.com/arangodb/kube-arangodb/pkg/util"
34+
"github.com/arangodb/kube-arangodb/pkg/util/arangod"
35+
ugrpc "github.com/arangodb/kube-arangodb/pkg/util/grpc"
36+
operatorHTTP "github.com/arangodb/kube-arangodb/pkg/util/http"
37+
)
38+
39+
func NewClient(endpoint, id, key string, mods ...util.Mod[goHttp.Transport]) (Client, error) {
40+
transport := operatorHTTP.Transport(mods...)
41+
42+
stageEndpoint := fmt.Sprintf("https://%s", endpoint)
43+
44+
connConfig := http.ConnectionConfig{
45+
Transport: transport,
46+
DontFollowRedirect: true,
47+
Endpoints: []string{stageEndpoint},
48+
}
49+
50+
conn, err := http.NewConnection(connConfig)
51+
if err != nil {
52+
return nil, err
53+
}
54+
55+
conn, err = conn.SetAuthentication(driver.BasicAuthentication(id, key))
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
return NewClientFromConn(conn), nil
61+
}
62+
63+
func NewClientFromConn(conn driver.Connection) Client {
64+
return client{
65+
conn: conn,
66+
}
67+
}
68+
69+
type Client interface {
70+
License(ctx context.Context, req LicenseRequest) (LicenseResponse, error)
71+
72+
Registry(ctx context.Context) (RegistryResponse, error)
73+
}
74+
75+
type LicenseRequest struct {
76+
DeploymentID *string `json:"deployment_id,omitempty"`
77+
TTL *time.Duration `json:"ttl,omitempty"`
78+
Inventory *ugrpc.Object[*inventory.Spec] `json:"inventory,omitempty"`
79+
}
80+
81+
type LicenseResponse struct {
82+
ID string `json:"id"`
83+
License string `json:"license"`
84+
}
85+
86+
type RegistryResponse struct {
87+
Token string `json:"token"`
88+
}
89+
90+
type client struct {
91+
conn driver.Connection
92+
}
93+
94+
func (c client) License(ctx context.Context, req LicenseRequest) (LicenseResponse, error) {
95+
return arangod.PostRequest[LicenseRequest, LicenseResponse](ctx, c.conn, req, "_api", "v1", "license").AcceptCode(200).Response()
96+
}
97+
98+
func (c client) Registry(ctx context.Context) (RegistryResponse, error) {
99+
return arangod.GetRequest[RegistryResponse](ctx, c.conn, "_api", "v1", "registry", "token").AcceptCode(200).Response()
100+
}

0 commit comments

Comments
 (0)