8000 [Feature] Scheduler CLI · arangodb/kube-arangodb@b715564 · GitHub
[go: up one dir, main page]

Skip to content 8000

Commit b715564

Browse files
committed
[Feature] Scheduler CLI
1 parent 612699c commit b715564

File tree

9 files changed

+817
-37
lines changed

9 files changed

+817
-37
lines changed

cmd/cmd.go

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ const (
7575
defaultServerPort = 8528
7676
defaultAPIHTTPPort = 8628
7777
defaultAPIGRPCPort = 8728
78-
defaultLogLevel = "info"
7978
defaultAdminSecretName = "arangodb-operator-dashboard"
8079
defaultAPIJWTSecretName = "arangodb-operator-api-jwt"
8180
defaultAPIJWTKeySecretName = "arangodb-operator-api-jwt-key"
@@ -96,9 +95,6 @@ var (
9695
hardLimit uint64
9796
}
9897

99-
logFormat string
100-
logLevels []string
101-
logSampling bool
10298
serverOptions struct {
10399
host string
104100
port int
@@ -195,9 +191,6 @@ func init() {
195191
f.StringVar(&serverOptions.tlsSecretName, "server.tls-secret-name", "", "Name of secret containing tls.crt & tls.key for HTTPS server (if empty, self-signed certificate is used)")
196192
f.StringVar(&serverOptions.adminSecretName, "server.admin-secret-name", defaultAdminSecretName, "Name of secret containing username + password for login to the dashboard")
197193
f.BoolVar(&serverOptions.allowAnonymous, "server.allow-anonymous-access", false, "Allow anonymous access to the dashboard")
198-
f.StringVar(&logFormat, "log.format", "pretty", "Set log format. Allowed values: 'pretty', 'JSON'. If empty, default format is used")
199-
f.StringArrayVar(&logLevels, "log.level", []string{defaultLogLevel}, fmt.Sprintf("Set log levels in format <level> or <logger>=<level>. Possible loggers: %s", strings.Join(logging.Global().Names(), ", ")))
200-
f.BoolVar(&logSampling, "log.sampling", true, "If true, operator will try to minimize duplication of logging events")
201194
f.BoolVar(&apiOptions.enabled, "api.enabled", true, "Enable operator HTTP and gRPC API")
202195
f.IntVar(&apiOptions.httpPort, "api.http-port", defaultAPIHTTPPort, "HTTP API port to listen on")
203196
f.IntVar(&apiOptions.grpcPort, "api.grpc-port", defaultAPIGRPCPort, "gRPC API port to listen on")
@@ -247,6 +240,9 @@ func init() {
247240
f.StringArrayVar(&metricsOptions.excludedMetricPrefixes, "metrics.excluded-prefixes", nil, "List of the excluded metrics prefixes")
248241
f.BoolVar(&operatorImageDiscovery.defaultStatusDiscovery, &qu 8000 ot;image.discovery.status", true, "Discover Operator Image from Pod Status by default. When disabled Pod Spec is used.")
249242
f.DurationVar(&operatorImageDiscovery.timeout, "image.discovery.timeout", time.Minute, "Timeout for image discovery process")
243+
if err := logging.Init(&cmdMain); err != nil {
244+
panic(err.Error())
245+
}
250246
if err := features.Init(&cmdMain); err != nil {
251247
panic(err.Error())
252248
}
@@ -308,24 +304,10 @@ func executeMain(cmd *cobra.Command, args []string) {
308304
kclient.SetDefaultBurst(operatorKubernetesOptions.burst)
309305

310306
// Prepare log service
311-
var err error
312-
313-
levels, err := logging.ParseLogLevelsFromArgs(logLevels)
314-
if err != nil {
315-
logger.Err(err).Fatal("Unable to parse log level")
307+
if err := logging.Enable(); err != nil {
308+
logger.Err(err).Fatal("Unable to enable logger")
316309
}
317310

318-
// Set root logger to stdout (JSON formatted) if not prettified
319-
if strings.ToUpper(logFormat) == "JSON" {
320-
logging.Global().SetRoot(zerolog.New(os.Stdout).With().Timestamp().Logger())
321-
} else if strings.ToLower(logFormat) != "pretty" && logFormat != "" {
322-
logger.Fatal("Unknown log format: %s", logFormat)
323-
}
324-
logging.Global().Configure(logging.Config{
325-
Levels: levels,
326-
Sampling: logSampling,
327-
})
328-
329311
podNameParts := strings.Split(name, "-")
330312
operatorID := podNameParts[len(podNameParts)-1]
331313

@@ -347,16 +329,16 @@ func executeMain(cmd *cobra.Command, args []string) {
347329
!operatorOptions.enableBackup && !operatorOptions.enableApps && !operatorOptions.enableK2KClusterSync && !operatorOptions.enableML {
348330
if !operatorOptions.versionOnly {
349331
if version.GetVersionV1().IsEnterprise() {
350-
logger.Err(err).Fatal("Turn on --operator.deployment, --operator.deployment-replication, --operator.storage, --operator.backup, --operator.apps, --operator.k2k-cluster-sync, --operator.ml or any combination of these")
332+
logger.Fatal("Turn on --operator.deployment, --operator.deployment-replication, --operator.storage, --operator.backup, --operator.apps, --operator.k2k-cluster-sync, --operator.ml or any combination of these")
351333
} else {
352-
logger.Err(err).Fatal("Turn on --operator.deployment, --operator.deployment-replication, --operator.storage, --operator.backup, --operator.apps, --operator.k2k-cluster-sync or any combination of these")
334+
logger.Fatal("Turn on --operator.deployment, --operator.deployment-replication, --operator.storage, --operator.backup, --operator.apps, --operator.k2k-cluster-sync or any combination of these")
353335
}
354336
}
355337
} else if operatorOptions.versionOnly {
356-
logger.Err(err).Fatal("Options --operator.deployment, --operator.deployment-replication, --operator.storage, --operator.backup, --operator.apps, --operator.k2k-cluster-sync, --operator.ml cannot be enabled together with --operator.version")
338+
logger.Fatal("Options --operator.deployment, --operator.deployment-replication, --operator.storage, --operator.backup, --operator.apps, --operator.k2k-cluster-sync, --operator.ml cannot be enabled together with --operator.version")
357339
} else if !version.GetVersionV1().IsEnterprise() {
358340
if operatorOptions.enableML {
359-
logger.Err(err).Fatal("Options --operator.ml can be enabled only on the Enterprise Operator")
341+
logger.Fatal("Options --operator.ml can be enabled only on the Enterprise Operator")
360342
}
361343
}
362344

@@ -444,7 +426,11 @@ func executeMain(cmd *cobra.Command, args []string) {
444426
if err != nil {
445427
logger.Err(err).Fatal("Failed to create API server")
446428
}
447-
go errors.LogError(logger, "while running API server", apiServer.Run)
429+
go func() {
430+
if err := apiServer.Run(); err != nil {
431+
logger.Err(err).Error("while running API server")
432+
}
433+
}()
448434
}
449435

450436
listenAddr := net.JoinHostPort(serverOptions.host, strconv.Itoa(serverOptions.port))
@@ -493,7 +479,11 @@ func executeMain(cmd *cobra.Command, args []string) {
493479
}); err != nil {
494480
logger.Err(err).Fatal("Failed to create HTTP server")
495481
} else {
496-
go errors.LogError(logger, "error while starting server", svr.Run)
482+
go func() {
483+
if err := svr.Run(); err != nil {
484+
logger.Err(err).Error("error while starting server")
485+
}
486+
}()
497487
}
498488

499489
// startChaos(context.Background(), cfg.KubeCli, cfg.Namespace, chaosLevel)

cmd/scheduler.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2024 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 cmd
22+
23+
import (
24+
"github.com/spf13/cobra"
25+
26+
"github.com/arangodb/kube-arangodb/pkg/scheduler"
27+
)
28+
29+
func init() {
30+
cmd := &cobra.Command{
31+
Use: "scheduler",
32+
}
33+
34+
if err := scheduler.InitCommand(cmd); err != nil {
35+
panic(err.Error())
36+
}
37+
38+
cmdMain.AddCommand(cmd)
39+
}

pkg/logging/cli.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2024 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 logging
22+
23+
import (
24+
"fmt"
25+
"os"
26+
"strings"
27+
"sync"
28+
29+
"github.com/rs/zerolog"
30+
"github.com/spf13/cobra"
31+
32+
"github.com/arangodb/kube-arangodb/pkg/util/errors"
33+
)
34+
35+
const (
36+
defaultLogLevel = "info"
37+
)
38+
39+
var (
40+
enableLock sync.Mutex
41+
enabled bool
42+
43+
cli struct {
44+
format string
45+
levels []string
46+
sampling bool
47+
}
48+
)
49+
50+
func Init(cmd *cobra.Command) error {
51+
f := cmd.PersistentFlags()
52+
53+
f.StringVar(&cli.format, "log.format", "pretty", "Set log format. Allowed values: 'pretty', 'JSON'. If empty, default format is used")
54+
f.StringArrayVar(&cli.levels, "log.level", []string{defaultLogLevel}, fmt.Sprintf("Set log levels in format <level> or <logger>=<level>. Possible loggers: %s", strings.Join(Global().Names(), ", ")))
55+
f.BoolVar(&cli.sampling, "log.sampling", true, "If true, operator will try to minimize duplication of logging events")
56+
57+
return nil
58+
}
59+
60+
func Enable() error {
61+
enableLock.Lock()
62+
defer enableLock.Unlock()
63+
64+
if enabled {
65+
return errors.Errorf("Logger already enabled")
66+
}
67+
68+
levels, err := ParseLogLevelsFromArgs(cli.levels)
69+
if err != nil {
70+
return errors.WithMessagef(err, "Unable to parse levels")
71+
}
72+
73+
// Set root logger to stdout (JSON formatted) if not prettified
74+
if strings.ToUpper(cli.format) == "JSON" {
75+
Global().SetRoot(zerolog.New(os.Stdout).With().Timestamp().Logger())
76+
} else if strings.ToLower(cli.format) != "pretty" && cli.format != "" {
77+
return errors.Errorf("Unknown log format: %s", cli.format)
78+
}
79+
Global().Configure(Config{
80+
Levels: levels,
81+
Sampling: cli.sampling,
82+
})
83+
84+
return nil
85+
}

pkg/scheduler/cli.go

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2024 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 scheduler
22+
23+
import (
24+
"context"
25+
"os"
26+
"strings"
27+
28+
"github.com/spf13/cobra"
29+
"sigs.k8s.io/yaml"
30+
31+
"github.com/arangodb/kube-arangodb/pkg/logging"
32+
"github.com/arangodb/kube-arangodb/pkg/util"
33+
"github.com/arangodb/kube-arangodb/pkg/util/constants"
34+
"github.com/arangodb/kube-arangodb/pkg/util/errors"
35+
"github.com/arangodb/kube-arangodb/pkg/util/kclient"
36+
)
37+
38+
func InitCommand(cmd *cobra.Command) error {
39+
var c cli
40+
return c.register(cmd)
41+
}
42+
43+
type cli struct {
44+
Namespace string
45+
46+
Labels []string
47+
Envs []string
48+
49+
Profiles []string
50+
51+
Container string
52+
53+
Image string
54+
}
55+
56+
func (c *cli) asRequest(args ...string) (Request, error) {
57+
var r = Request{
58+
Labels: map[string]string{},
59+
Envs: map[string]string{},
60+
}
61+
62+
for _, l := range c.Labels {
63+
p := strings.SplitN(l, "=", 2)
64+
if len(p) == 1 {
65+
r.Labels[p[0]] = ""
66+
logger.Debug("Label Discovered: %s", p[0])
67+
} else {
68+
r.Labels[p[0]] = p[1]
69+
logger.Debug("Label Discovered: %s=%s", p[0], p[1])
70+
}
71+
}
72+
73+
for _, l := range c.Envs {
74+
p := strings.SplitN(l, "=", 2)
75+
if len(p) == 1 {
76+
return r, errors.Errorf("Missing value for env: %s", p[0])
77+
} else {
78+
r.Envs[p[0]] = p[1]
79+
logger.Debug("Env Discovered: %s=%s", p[0], p[1])
80+
}
81+
}
82+
83+
if len(c.Profiles) > 0 {
84+
r.Profiles = c.Profiles
85+
logger.Debug("Enabling profiles: %s", strings.Join(c.Profiles, ", "))
86+
}
87+
88+
r.Container = util.NewType(c.Container)
89+
if c.Image != "" {
90+
r.Image = util.NewType(c.Image)
91+
}
92+
93+
r.Args = args
94+
95+
return r, nil
96+
}
97+
98+
func (c *cli) register(cmd *cobra.Command) error {
99+
if err := logging.Init(cmd); err != nil {
100+
return err
101+
}
102+
103+
cmd.RunE = c.run
104+
105+
f := cmd.PersistentFlags()
106+
107+
f.StringVarP(&c.Namespace, "namespace", "n", constants.NamespaceWithDefault("default"), "Kubernetes namespace")
108+
f.StringSliceVarP(&c.Labels, "label", "l", nil, "Scheduler Render Labels in format <key>=<value>")
109+
f.StringSliceVarP(&c.Envs, "env", "e", nil, "Scheduler Render Envs in format <key>=<value>")
110+
f.StringSliceVarP(&c.Profiles, "profile", "p", nil, "Scheduler Render Profiles")
111+
f.StringVar(&c.Container, "container", DefaultContainerName, "Container Name")
112+
f.StringVar(&c.Image, "image", "", "Image")
113+
114+
return nil
115+
}
116+
117+
func (c *cli) run(cmd *cobra.Command, args []string) error {
118+
if err := logging.Enable(); err != nil {
119+
return err
120+
}
121+
122+
r, err := c.asRequest()
123+
if err != nil {
124+
return err
125+
}
126+
127+
k, ok := kclient.GetDefaultFactory().Client()
128+
if !ok {
129+
return errors.Errorf("Unable to create Kubernetes Client")
130+
}
131+
132+
s := NewScheduler(k, c.Namespace)
133+
134+
rendered, profiles, err := s.Render(context.Background(), r)
135+
if err != nil {
136+
return err
137+
}
138+
logger.Debug("Enabled profiles: %s", strings.Join(profiles, ", "))
139+
140+
data, err := yaml.Marshal(rendered)
141+
if err != nil {
142+
return err
143+
}
144+
145+
if _, err := util.WriteAll(os.Stdout, data); err != nil {
146+
return err
147+
}
148+
149+
return nil
150+
}

0 commit comments

Comments
 (0)
0