Skip to content

Commit 3e97414

Browse files
committed
cli: create etcd command
This commit introduces the `etcd` comand under `run`. It is not active yet as it will be in later commits. Signed-off-by: Elis Lulja <[email protected]>
1 parent dee9e7c commit 3e97414

File tree

5 files changed

+266
-1
lines changed

5 files changed

+266
-1
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ require (
8181
sigs.k8s.io/yaml v1.2.0 // indirect
8282
)
8383

84+
require golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
85+
8486
require (
8587
github.com/aws/aws-sdk-go-v2/credentials v1.12.14 // indirect
8688
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.12 // indirect

go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,7 @@ golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBc
772772
golang.org/x/sys v0.0.0-20220624220833-87e55d714810 h1:rHZQSjJdAI4Xf5Qzeh2bBc5YJIkPFVM6oDtMFYmgws0=
773773
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
774774
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
775+
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
775776
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
776777
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
777778
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

pkg/cluster/kubernetes.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,17 @@ func GetFilesFromSecret(ctx context.Context, nsName, name string) ([][]byte, err
201201

202202
return data, nil
203203
}
204+
205+
func GetDataFromSecret(ctx context.Context, nsName, name string) (map[string][]byte, error) {
206+
cli, err := getK8sClientSet()
207+
if err != nil {
208+
return nil, err
209+
}
210+
211+
secret, err := cli.CoreV1().Secrets(nsName).Get(ctx, name, metav1.GetOptions{})
212+
if err != nil {
213+
return nil, err
214+
}
215+
216+
return secret.Data, nil
217+
}

pkg/command/run/etcd.go

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
// Copyright © 2022 Cisco
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
//
17+
// All rights reserved.
18+
19+
package run
20+
21+
import (
22+
"context"
23+
"fmt"
24+
"io/ioutil"
25+
"strings"
26+
"syscall"
27+
"time"
28+
29+
"github.com/CloudNativeSDWAN/cnwan-operator/pkg/cluster"
30+
"github.com/CloudNativeSDWAN/cnwan-operator/pkg/servregistry/etcd"
31+
"github.com/davecgh/go-spew/spew"
32+
"github.com/spf13/cobra"
33+
clientv3 "go.etcd.io/etcd/client/v3"
34+
"golang.org/x/term"
35+
"gopkg.in/yaml.v3"
36+
)
37+
38+
const (
39+
defaultEtcdConfigMapName = "etcd-options"
40+
defaultEtcdCredentialsSecretName = "etcd-credentials"
41+
)
42+
43+
type EtcdOptions struct {
44+
Prefix string `yaml:"prefix"`
45+
Endpoints []string `yaml:"endpoints"`
46+
47+
Username string
48+
Password string
49+
}
50+
51+
func getRunEtcdCommand(operatorOpts *Options) *cobra.Command {
52+
// -----------------------------
53+
// Inits and defaults
54+
// -----------------------------
55+
56+
opts := &EtcdOptions{}
57+
var (
58+
optsPath string
59+
optsConfigMap string
60+
// This is used for the --password flag option, to signal user has
61+
// indeed a password for authentication.
62+
tmpPassFlag bool
63+
credentialsSecret string
64+
)
65+
66+
// -----------------------------
67+
// The command
68+
// -----------------------------
69+
70+
cmd := &cobra.Command{
71+
Use: "etcd [COMMAND] [OPTIONS]",
72+
Short: "Run the program with etcd",
73+
PreRunE: func(cmd *cobra.Command, args []string) error {
74+
l := log.With().Str("cmd", "etcd").Logger()
75+
76+
// -- Get the options from file or ConfigMap
77+
if optsPath != "" || optsConfigMap != "" {
78+
var (
79+
fileOptions []byte
80+
decodedFileOptions *EtcdOptions
81+
)
82+
83+
// -- Get the options from path
84+
if optsPath != "" {
85+
if optsConfigMap != "" {
86+
l.Warn().Msg("both path and configmap flags are provided: only the path will be used")
87+
optsConfigMap = ""
88+
}
89+
90+
log.Debug().Str("path", optsPath).
91+
Msg("getting options from file...")
92+
byteOpts, err := ioutil.ReadFile(optsPath)
93+
if err != nil {
94+
return fmt.Errorf("cannot open file %s: %w", optsPath, err)
95+
}
96+
97+
fileOptions = byteOpts
98+
}
99+
100+
// -- Get options from configmap
101+
if optsConfigMap != "" {
102+
log.Debug().
103+
Str("namespace", operatorOpts.Namespace).
104+
Str("name", optsConfigMap).
105+
Msg("getting options from configmap...")
106+
107+
ctx, canc := context.WithTimeout(context.Background(), 10*time.Second)
108+
cfg, err := cluster.GetFilesFromConfigMap(ctx, operatorOpts.Namespace, optsConfigMap)
109+
if err != nil {
110+
canc()
111+
return fmt.Errorf("cannot get configmap: %w", err)
112+
}
113+
canc()
114+
115+
fileOptions = cfg[0]
116+
}
117+
118+
if len(fileOptions) > 0 {
119+
if err := yaml.Unmarshal(fileOptions, &decodedFileOptions); err != nil {
120+
return fmt.Errorf("cannot decode options %s: %w", optsPath, err)
121+
}
122+
}
123+
124+
if !cmd.Flag("endpoints").Changed {
125+
opts.Endpoints = decodedFileOptions.Endpoints
126+
}
127+
128+
if !cmd.Flag("prefix").Changed {
129+
opts.Prefix = decodedFileOptions.Prefix
130+
}
131+
}
132+
133+
// -- Get the credentials secret
134+
if credentialsSecret != "" {
135+
l.Debug().
136+
Str("namespace", operatorOpts.Namespace).
137+
Str("name", credentialsSecret).
138+
Msg("getting credentials from secret...")
139+
140+
ctx, canc := context.WithTimeout(context.Background(), 10*time.Second)
141+
secret, err := cluster.GetDataFromSecret(ctx, operatorOpts.Namespace, credentialsSecret)
142+
if err != nil {
143+
canc()
144+
return fmt.Errorf("cannot get secret: %w", err)
145+
}
146+
canc()
147+
148+
username, exists := secret["username"]
149+
if !exists {
150+
return fmt.Errorf("secret %s does not contain any username", credentialsSecret)
151+
} else {
152+
if opts.Username == "" {
153+
// The user did not overwrite this via flag.
154+
// So we're going to the value from the secret.
155+
opts.Username = string(username)
156+
}
157+
}
158+
159+
if password, exists := secret["password"]; exists {
160+
opts.Password = string(password)
161+
}
162+
}
163+
164+
// -- Ask for password
165+
if tmpPassFlag {
166+
if operatorOpts.RunningInK8s {
167+
return fmt.Errorf("cannot use --password while running in Kubernetes. Please use a Secret instead.")
168+
}
169+
170+
fmt.Printf("Please enter password for %s: ", opts.Username)
171+
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
172+
if err != nil {
173+
return fmt.Errorf("cannot read password from terminal: %w", err)
174+
}
175+
fmt.Println()
176+
177+
opts.Password = strings.TrimSpace(string(bytePassword))
178+
}
179+
180+
return nil
181+
},
182+
RunE: func(_ *cobra.Command, _ []string) error {
183+
return runWithEtcd(operatorOpts, opts)
184+
},
185+
Example: "etcd --username root -p --endpoints 10.10.10.10:2379,10.10.10.11:2379",
186+
}
187+
188+
// -----------------------------
189+
// Flags
190+
// -----------------------------
191+
192+
cmd.Flags().StringVarP(&opts.Username, "username", "u", "",
193+
"username for authentication")
194+
cmd.Flags().BoolVarP(&tmpPassFlag, "password", "p", false,
195+
"enter password -- will be done interactively.")
196+
cmd.Flags().StringVar(&opts.Prefix, "prefix", "",
197+
"prefix to insert before every key.")
198+
cmd.Flags().StringSliceVar(&opts.Endpoints, "endpoints", []string{"localhost:2379"},
199+
"list of endpoints for etcd.")
200+
cmd.Flags().StringVar(&optsPath, "options-path", "",
201+
"path to the file containing service directory options.")
202+
cmd.Flags().StringVar(&optsConfigMap, "options-configmap", func() string {
203+
if operatorOpts.RunningInK8s {
204+
return defaultEtcdConfigMapName
205+
}
206+
207+
return ""
208+
}(),
209+
"name of the Kubernetes config map containing settings.")
210+
cmd.Flags().StringVar(&credentialsSecret, "credentials-secret", func() string {
211+
if operatorOpts.RunningInK8s {
212+
return defaultEtcdCredentialsSecretName
213+
}
214+
215+
return ""
216+
}(),
217+
"name of the Kubernetes secret containing the credentials.")
218+
219+
return cmd
220+
}
221+
222+
func runWithEtcd(operatorOpts *Options, opts *EtcdOptions) error {
223+
ctx, canc := context.WithTimeout(context.Background(), 15*time.Second)
224+
defer canc()
225+
226+
spew.Dump(opts)
227+
228+
// TODO: support certificates
229+
cli, err := clientv3.New(clientv3.Config{
230+
Endpoints: opts.Endpoints,
231+
Username: opts.Username,
232+
Password: opts.Password,
233+
DialTimeout: 15 * time.Second,
234+
})
235+
236+
if err != nil {
237+
return fmt.Errorf("cannot get etcd client: %w", err)
238+
}
239+
240+
defer cli.Close()
241+
242+
servreg := etcd.NewServiceRegistryWithEtcd(ctx, cli, &opts.Prefix)
243+
244+
// TODO: use the handler
245+
_ = servreg
246+
247+
return nil
248+
}

pkg/command/run/run.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ func GetRunCommand() *cobra.Command {
348348
// Sub commands
349349
// -----------------------------
350350

351-
// TODO: add commands
351+
cmd.AddCommand(getRunEtcdCommand(opts))
352352

353353
return cmd
354354
}

0 commit comments

Comments
 (0)