Skip to content

Commit 8e12c93

Browse files
committed
cli: create run cloudmap command
This commit introduces the `cloudmap` subcommand that allows the operator to perform operations on AWS Cloud Map. It is not active yet, and will be in future commits. Signed-off-by: Elis Lulja <elulja@cisco.com>
1 parent e62998d commit 8e12c93

2 files changed

Lines changed: 211 additions & 1 deletion

File tree

pkg/command/run/cloud_map.go

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
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+
"os"
26+
"path"
27+
"time"
28+
29+
"github.com/CloudNativeSDWAN/cnwan-operator/pkg/servregistry/aws/cloudmap"
30+
"github.com/aws/aws-sdk-go-v2/config"
31+
"github.com/aws/aws-sdk-go-v2/service/servicediscovery"
32+
"github.com/spf13/cobra"
33+
"gopkg.in/yaml.v3"
34+
ctrl "sigs.k8s.io/controller-runtime"
35+
"sigs.k8s.io/controller-runtime/pkg/log/zap"
36+
)
37+
38+
const (
39+
defaultCloudMapConfigMapName = "cloud-map-options"
40+
defaultCloudMapCredentialsSecretName = "cloud-map-credentials"
41+
)
42+
43+
type CloudMapOptions struct {
44+
DefaultRegion string `yaml:"defaultRegion"`
45+
46+
credentialsBytes []byte
47+
}
48+
49+
func getRunCloudMapCommand(operatorOpts *Options) *cobra.Command {
50+
// -----------------------------
51+
// Inits and defaults
52+
// -----------------------------
53+
54+
opts := &CloudMapOptions{}
55+
cmFileOpts := &fileOrK8sResource{}
56+
credsOpts := &fileOrK8sResource{}
57+
58+
// -----------------------------
59+
// The command
60+
// -----------------------------
61+
62+
cmd := &cobra.Command{
63+
Use: "cloudmap [COMMAND] [OPTIONS]",
64+
Aliases: []string{"cm", "aws-cloud-map", "with-cloud-map"},
65+
Short: "Run the program with AWS Cloud Map",
66+
PreRunE: func(cmd *cobra.Command, args []string) error {
67+
// -- Parse the options
68+
if err := parseCloudMapCommand(cmFileOpts, opts, operatorOpts, cmd); err != nil {
69+
return fmt.Errorf("error while parsing cloud map command: %w", err)
70+
}
71+
72+
// -- Get the credentials file
73+
if credsOpts.path == "" && credsOpts.k8s == "" {
74+
homeDir, err := os.UserHomeDir()
75+
if err != nil {
76+
return fmt.Errorf("cannot detect user home directory: %w", err)
77+
}
78+
79+
credsOpts.path = path.Join(homeDir, ".aws", "credentials")
80+
}
81+
82+
var k8sres *k8sResource
83+
if credsOpts.k8s != "" {
84+
k8sres = &k8sResource{
85+
Type: "secret",
86+
Namespace: operatorOpts.Namespace,
87+
Name: credsOpts.k8s,
88+
}
89+
}
90+
credentialsBytes, err := getFileFromPathOrK8sResource(credsOpts.path, k8sres)
91+
if err != nil {
92+
return fmt.Errorf("cannot get credentials for cloud metadata: %w", err)
93+
}
94+
95+
opts.credentialsBytes = credentialsBytes
96+
97+
return nil
98+
},
99+
RunE: func(_ *cobra.Command, _ []string) error {
100+
return runWithCloudMap(operatorOpts, opts)
101+
},
102+
Example: "cloudmap --default-region us-east-1",
103+
}
104+
105+
// -----------------------------
106+
// Flags
107+
// -----------------------------
108+
109+
cmd.Flags().StringVar(&opts.DefaultRegion, "default-region", "",
110+
"region/location where to register resources.")
111+
cmd.Flags().StringVar(&cmFileOpts.path, "cloud-map.options-path", "",
112+
"path to the file containing cloud map options.")
113+
cmd.Flags().StringVar(&cmFileOpts.k8s, "cloud-map.options-configmap", func() string {
114+
if operatorOpts.RunningInK8s {
115+
return defaultCloudMapConfigMapName
116+
}
117+
118+
return ""
119+
}(),
120+
"name of the Kubernetes config map containing settings.")
121+
cmd.Flags().StringVar(&credsOpts.path, "credentials-path", "",
122+
"path to the credentials file.")
123+
cmd.Flags().StringVar(&credsOpts.k8s, "credentials-secret", func() string {
124+
if operatorOpts.RunningInK8s {
125+
return defaultCloudMapCredentialsSecretName
126+
}
127+
128+
return ""
129+
}(),
130+
"name of the Kubernetes secret containing the credentials.")
131+
132+
return cmd
133+
}
134+
135+
func parseCloudMapCommand(flagOpts *fileOrK8sResource, cmOpts *CloudMapOptions, operatorOpts *Options, cmd *cobra.Command) error {
136+
// --------------------------------
137+
// Get options from path or configmap
138+
// --------------------------------
139+
140+
if flagOpts.path != "" || flagOpts.k8s != "" {
141+
var (
142+
fileOptions []byte
143+
decodedFileOptions *CloudMapOptions
144+
)
145+
146+
var k8sres *k8sResource
147+
if flagOpts.k8s != "" {
148+
k8sres = &k8sResource{
149+
Type: "configmap",
150+
Namespace: operatorOpts.Namespace,
151+
Name: flagOpts.k8s,
152+
}
153+
}
154+
155+
fileOptions, err := getFileFromPathOrK8sResource(flagOpts.path, k8sres)
156+
if err != nil {
157+
return fmt.Errorf("could not load options: %w", err)
158+
}
159+
160+
// Unmarshal the file and put the values found inside into our main
161+
// options struct...
162+
if err := yaml.Unmarshal(fileOptions, &decodedFileOptions); err != nil {
163+
return fmt.Errorf("cannot decode options %s: %w", flagOpts.path, err)
164+
}
165+
166+
// ... unless cmd flags are provided. In which case they (the flags)
167+
// take precedence.
168+
if !cmd.Flag("default-region").Changed {
169+
cmOpts.DefaultRegion = decodedFileOptions.DefaultRegion
170+
}
171+
}
172+
173+
// -- Are the settings provided at least?
174+
if cmOpts.DefaultRegion == "" {
175+
return fmt.Errorf("no region provided")
176+
}
177+
178+
return nil
179+
}
180+
181+
func runWithCloudMap(operatorOpts *Options, cmOpts *CloudMapOptions) error {
182+
ctx, canc := context.WithTimeout(context.Background(), 15*time.Second)
183+
defer canc()
184+
const tempPath = "/tmp/cnwan-operator-aws-credentials"
185+
186+
opts := []func(*config.LoadOptions) error{config.WithRegion(cmOpts.DefaultRegion)}
187+
if err := ioutil.WriteFile(tempPath, cmOpts.credentialsBytes, 0644); err != nil {
188+
return fmt.Errorf("error while trying to write credentials to temporary path: %w", err)
189+
}
190+
191+
opts = append(opts, config.WithSharedCredentialsFiles([]string{tempPath}))
192+
defer os.Remove(tempPath)
193+
194+
cfg, err := config.LoadDefaultConfig(ctx, opts...)
195+
if err != nil {
196+
return fmt.Errorf("error while trying to load AWS configuration: %w", err)
197+
}
198+
199+
// TODO: when #81 is solved an merged this will be replaced
200+
// with zerolog
201+
l := ctrl.Log.WithName("ServiceDirectory")
202+
ctrl.SetLogger(zap.New(zap.UseDevMode(true)))
203+
204+
servreg := cloudmap.NewHandler(ctx, servicediscovery.NewFromConfig(cfg), l)
205+
206+
// TODO: use the handler (in next commits)
207+
_ = servreg
208+
209+
return nil
210+
}

pkg/command/run/run.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ func GetRunCommand() *cobra.Command {
166166
// Sub commands
167167
// -----------------------------
168168

169-
// TODO: add commands
169+
cmd.AddCommand(getRunCloudMapCommand(opts))
170170

171171
return cmd
172172
}

0 commit comments

Comments
 (0)