-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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]>
- Loading branch information
1 parent
dee9e7c
commit 3e97414
Showing
5 changed files
with
266 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,248 @@ | ||
// Copyright © 2022 Cisco | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
// All rights reserved. | ||
|
||
package run | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io/ioutil" | ||
"strings" | ||
"syscall" | ||
"time" | ||
|
||
"github.com/CloudNativeSDWAN/cnwan-operator/pkg/cluster" | ||
"github.com/CloudNativeSDWAN/cnwan-operator/pkg/servregistry/etcd" | ||
"github.com/davecgh/go-spew/spew" | ||
"github.com/spf13/cobra" | ||
clientv3 "go.etcd.io/etcd/client/v3" | ||
"golang.org/x/term" | ||
"gopkg.in/yaml.v3" | ||
) | ||
|
||
const ( | ||
defaultEtcdConfigMapName = "etcd-options" | ||
defaultEtcdCredentialsSecretName = "etcd-credentials" | ||
) | ||
|
||
type EtcdOptions struct { | ||
Prefix string `yaml:"prefix"` | ||
Endpoints []string `yaml:"endpoints"` | ||
|
||
Username string | ||
Password string | ||
} | ||
|
||
func getRunEtcdCommand(operatorOpts *Options) *cobra.Command { | ||
// ----------------------------- | ||
// Inits and defaults | ||
// ----------------------------- | ||
|
||
opts := &EtcdOptions{} | ||
var ( | ||
optsPath string | ||
optsConfigMap string | ||
// This is used for the --password flag option, to signal user has | ||
// indeed a password for authentication. | ||
tmpPassFlag bool | ||
credentialsSecret string | ||
) | ||
|
||
// ----------------------------- | ||
// The command | ||
// ----------------------------- | ||
|
||
cmd := &cobra.Command{ | ||
Use: "etcd [COMMAND] [OPTIONS]", | ||
Short: "Run the program with etcd", | ||
PreRunE: func(cmd *cobra.Command, args []string) error { | ||
l := log.With().Str("cmd", "etcd").Logger() | ||
|
||
// -- Get the options from file or ConfigMap | ||
if optsPath != "" || optsConfigMap != "" { | ||
var ( | ||
fileOptions []byte | ||
decodedFileOptions *EtcdOptions | ||
) | ||
|
||
// -- Get the options from path | ||
if optsPath != "" { | ||
if optsConfigMap != "" { | ||
l.Warn().Msg("both path and configmap flags are provided: only the path will be used") | ||
optsConfigMap = "" | ||
} | ||
|
||
log.Debug().Str("path", optsPath). | ||
Msg("getting options from file...") | ||
byteOpts, err := ioutil.ReadFile(optsPath) | ||
if err != nil { | ||
return fmt.Errorf("cannot open file %s: %w", optsPath, err) | ||
} | ||
|
||
fileOptions = byteOpts | ||
} | ||
|
||
// -- Get options from configmap | ||
if optsConfigMap != "" { | ||
log.Debug(). | ||
Str("namespace", operatorOpts.Namespace). | ||
Str("name", optsConfigMap). | ||
Msg("getting options from configmap...") | ||
|
||
ctx, canc := context.WithTimeout(context.Background(), 10*time.Second) | ||
cfg, err := cluster.GetFilesFromConfigMap(ctx, operatorOpts.Namespace, optsConfigMap) | ||
if err != nil { | ||
canc() | ||
return fmt.Errorf("cannot get configmap: %w", err) | ||
} | ||
canc() | ||
|
||
fileOptions = cfg[0] | ||
} | ||
|
||
if len(fileOptions) > 0 { | ||
if err := yaml.Unmarshal(fileOptions, &decodedFileOptions); err != nil { | ||
return fmt.Errorf("cannot decode options %s: %w", optsPath, err) | ||
} | ||
} | ||
|
||
if !cmd.Flag("endpoints").Changed { | ||
opts.Endpoints = decodedFileOptions.Endpoints | ||
} | ||
|
||
if !cmd.Flag("prefix").Changed { | ||
opts.Prefix = decodedFileOptions.Prefix | ||
} | ||
} | ||
|
||
// -- Get the credentials secret | ||
if credentialsSecret != "" { | ||
l.Debug(). | ||
Str("namespace", operatorOpts.Namespace). | ||
Str("name", credentialsSecret). | ||
Msg("getting credentials from secret...") | ||
|
||
ctx, canc := context.WithTimeout(context.Background(), 10*time.Second) | ||
secret, err := cluster.GetDataFromSecret(ctx, operatorOpts.Namespace, credentialsSecret) | ||
if err != nil { | ||
canc() | ||
return fmt.Errorf("cannot get secret: %w", err) | ||
} | ||
canc() | ||
|
||
username, exists := secret["username"] | ||
if !exists { | ||
return fmt.Errorf("secret %s does not contain any username", credentialsSecret) | ||
} else { | ||
if opts.Username == "" { | ||
// The user did not overwrite this via flag. | ||
// So we're going to the value from the secret. | ||
opts.Username = string(username) | ||
} | ||
} | ||
|
||
if password, exists := secret["password"]; exists { | ||
opts.Password = string(password) | ||
} | ||
} | ||
|
||
// -- Ask for password | ||
if tmpPassFlag { | ||
if operatorOpts.RunningInK8s { | ||
return fmt.Errorf("cannot use --password while running in Kubernetes. Please use a Secret instead.") | ||
} | ||
|
||
fmt.Printf("Please enter password for %s: ", opts.Username) | ||
bytePassword, err := term.ReadPassword(int(syscall.Stdin)) | ||
if err != nil { | ||
return fmt.Errorf("cannot read password from terminal: %w", err) | ||
} | ||
fmt.Println() | ||
|
||
opts.Password = strings.TrimSpace(string(bytePassword)) | ||
} | ||
|
||
return nil | ||
}, | ||
RunE: func(_ *cobra.Command, _ []string) error { | ||
return runWithEtcd(operatorOpts, opts) | ||
}, | ||
Example: "etcd --username root -p --endpoints 10.10.10.10:2379,10.10.10.11:2379", | ||
} | ||
|
||
// ----------------------------- | ||
// Flags | ||
// ----------------------------- | ||
|
||
cmd.Flags().StringVarP(&opts.Username, "username", "u", "", | ||
"username for authentication") | ||
cmd.Flags().BoolVarP(&tmpPassFlag, "password", "p", false, | ||
"enter password -- will be done interactively.") | ||
cmd.Flags().StringVar(&opts.Prefix, "prefix", "", | ||
"prefix to insert before every key.") | ||
cmd.Flags().StringSliceVar(&opts.Endpoints, "endpoints", []string{"localhost:2379"}, | ||
"list of endpoints for etcd.") | ||
cmd.Flags().StringVar(&optsPath, "options-path", "", | ||
"path to the file containing service directory options.") | ||
cmd.Flags().StringVar(&optsConfigMap, "options-configmap", func() string { | ||
if operatorOpts.RunningInK8s { | ||
return defaultEtcdConfigMapName | ||
} | ||
|
||
return "" | ||
}(), | ||
"name of the Kubernetes config map containing settings.") | ||
cmd.Flags().StringVar(&credentialsSecret, "credentials-secret", func() string { | ||
if operatorOpts.RunningInK8s { | ||
return defaultEtcdCredentialsSecretName | ||
} | ||
|
||
return "" | ||
}(), | ||
"name of the Kubernetes secret containing the credentials.") | ||
|
||
return cmd | ||
} | ||
|
||
func runWithEtcd(operatorOpts *Options, opts *EtcdOptions) error { | ||
ctx, canc := context.WithTimeout(context.Background(), 15*time.Second) | ||
defer canc() | ||
|
||
spew.Dump(opts) | ||
|
||
// TODO: support certificates | ||
cli, err := clientv3.New(clientv3.Config{ | ||
Endpoints: opts.Endpoints, | ||
Username: opts.Username, | ||
Password: opts.Password, | ||
DialTimeout: 15 * time.Second, | ||
}) | ||
|
||
if err != nil { | ||
return fmt.Errorf("cannot get etcd client: %w", err) | ||
} | ||
|
||
defer cli.Close() | ||
|
||
servreg := etcd.NewServiceRegistryWithEtcd(ctx, cli, &opts.Prefix) | ||
|
||
// TODO: use the handler | ||
_ = servreg | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters