1 package modifyclusterconfig
2
3 import (
4 "context"
5 "fmt"
6 "regexp"
7 "strings"
8 "time"
9
10 "edge-infra.dev/pkg/edge/api/graph/model"
11 "edge-infra.dev/pkg/edge/api/utils"
12 "edge-infra.dev/pkg/edge/edgeadmin/edgecliutils"
13 "edge-infra.dev/pkg/edge/edgecli"
14 "edge-infra.dev/pkg/edge/edgecli/constructors"
15 "edge-infra.dev/pkg/edge/edgecli/flagutil"
16 "edge-infra.dev/pkg/lib/cli/command"
17 "edge-infra.dev/pkg/lib/cli/rags"
18 )
19
20 func NewCmd(cfg *edgecli.Config) *command.Command {
21 var cmd *command.Command
22 flagsets := flagutil.GetConnectionFlags(cfg)
23 var storeName, bannerName string
24 config := model.UpdateClusterConfig{}
25
26 cmd = &command.Command{
27 ShortUsage: "edge clusterconfig modify",
28 ShortHelp: "Update cluster config",
29 Flags: append(
30 flagsets,
31 &rags.Rag{
32 Name: flagutil.StoreFlag,
33 Usage: "Name of the Store",
34 Value: &rags.String{Var: &storeName},
35 Required: true,
36 },
37 &rags.Rag{
38 Name: flagutil.BannerFlag,
39 Usage: "Banner name",
40 Value: &rags.String{Var: &bannerName},
41 Required: true,
42 },
43 &rags.Rag{
44 Name: flagutil.PxeEnabledFlag,
45 Value: &rags.Bool{Var: config.PxeEnabled},
46 Usage: "Enable pxe booting",
47 Required: false,
48 },
49 &rags.Rag{
50 Name: flagutil.AcRelayFlag,
51 Value: &rags.Bool{Var: config.AcRelay},
52
53 Usage: "Enable activation code relaying for pxe-boot",
54 Required: false,
55 },
56 &rags.Rag{
57 Name: edgecliutils.BootstrapAckFlag,
58 Value: &rags.Bool{Var: config.BootstrapAck},
59 Usage: "Bootstrap acknowledgement - require user confirmation for terminal bootstrap",
60 Required: false,
61 },
62 &rags.Rag{
63 Name: flagutil.VpnEnabledmentFlag,
64 Value: &rags.Bool{Var: config.VpnEnabled},
65 Usage: "Enable vpn access on the cluster",
66 Required: false,
67 },
68 &rags.Rag{
69 Name: flagutil.ThickPosFlag,
70 Value: &rags.Bool{Var: config.ThickPos, Default: true},
71 Usage: "Enable thick pos mode",
72 Required: false,
73 },
74 &rags.Rag{
75 Name: flagutil.EgressGatewayFlag,
76 Value: &rags.Bool{Var: config.EgressGatewayEnabled},
77 Usage: "Enable egress gateway",
78 Required: false,
79 },
80 &rags.Rag{
81 Name: flagutil.GatewayRateLimitingFlag,
82 Value: &rags.Bool{Var: config.GatewayRateLimitingEnabled},
83 Usage: "Enable gateway rate limiting",
84 Required: false,
85 },
86 &rags.Rag{
87 Name: flagutil.UplinkRateLimitFlag,
88 Value: &rags.String{Var: config.UplinkRateLimit},
89 Usage: "A limit on the rate data can be uploaded",
90 Required: false,
91 },
92 &rags.Rag{
93 Name: flagutil.DownlinkRateLimitFlag,
94 Value: &rags.String{Var: config.DownlinkRateLimit},
95 Usage: "A limit on the rate date can be downloaded",
96 Required: false,
97 },
98 &rags.Rag{
99 Name: flagutil.ClusterLogLevel,
100 Value: &rags.String{Var: config.ClusterLogLevel},
101 Usage: "Cluster Level Log level for severity filtering",
102 Required: false,
103 },
104 &rags.Rag{
105 Name: flagutil.MaximumLanOutageHours,
106 Value: &rags.Int{Var: config.MaximumLanOutageHours, Default: 96},
107 Usage: "Maximum LAN outage duration in hours",
108 Required: false,
109 },
110 ),
111 Exec: func(_ context.Context, _ []string) error {
112
113 if err := flagutil.ValidateRequiredFlags(cmd.Rags); err != nil {
114 return err
115 }
116
117 registrar, err := constructors.BuildRegistrar(cmd.Rags)
118 if err != nil {
119 return err
120 }
121
122 reqCtx, cancelReq := context.WithTimeout(context.Background(), time.Duration(30)*time.Second)
123 defer cancelReq()
124
125 cluster, err := registrar.GetCluster(reqCtx, storeName, bannerName)
126 if err != nil {
127 return err
128 }
129
130 if flagutil.GetBoolFlag(cmd.Rags, flagutil.AcRelayFlag) {
131 acRelay := flagutil.GetBoolFlag(cmd.Rags, flagutil.AcRelayFlag)
132 config.AcRelay = &acRelay
133 }
134
135 if flagutil.GetBoolFlag(cmd.Rags, flagutil.PxeEnabledFlag) {
136 pxeEnabled := flagutil.GetBoolFlag(cmd.Rags, flagutil.PxeEnabledFlag)
137 config.PxeEnabled = &pxeEnabled
138 }
139
140 if flagutil.GetBoolFlag(cmd.Rags, flagutil.BootstrapAckFlag) {
141 bootstrapAck := flagutil.GetBoolFlag(cmd.Rags, flagutil.BootstrapAckFlag)
142 config.BootstrapAck = &bootstrapAck
143 }
144
145 if flagutil.GetBoolFlag(cmd.Rags, flagutil.VpnEnabledmentFlag) {
146 vpnEnabled := flagutil.GetBoolFlag(cmd.Rags, flagutil.VpnEnabledmentFlag)
147 config.VpnEnabled = &vpnEnabled
148 }
149
150 if flagutil.GetBoolFlag(cmd.Rags, flagutil.ThickPosFlag) {
151 thickPos := flagutil.GetBoolFlag(cmd.Rags, flagutil.ThickPosFlag)
152 config.ThickPos = &thickPos
153 }
154
155 if flagutil.GetBoolFlag(cmd.Rags, flagutil.EgressGatewayFlag) {
156 egressGatewayEnabled := flagutil.GetBoolFlag(cmd.Rags, flagutil.EgressGatewayFlag)
157 config.EgressGatewayEnabled = &egressGatewayEnabled
158 }
159
160 if flagutil.GetBoolFlag(cmd.Rags, flagutil.GatewayRateLimitingFlag) {
161 gatewayRateLimitingEnabled := flagutil.GetBoolFlag(cmd.Rags, flagutil.GatewayRateLimitingFlag)
162 config.GatewayRateLimitingEnabled = &gatewayRateLimitingEnabled
163 }
164
165 if flagutil.GetStringFlag(cmd.Rags, flagutil.UplinkRateLimitFlag) != "" {
166 uplinkRateLimit := flagutil.GetStringFlag(cmd.Rags, flagutil.UplinkRateLimitFlag)
167 config.UplinkRateLimit = &uplinkRateLimit
168 if err := validateRateLimit(config.UplinkRateLimit); err != nil && config.UplinkRateLimit != nil {
169 return fmt.Errorf("uplink validation failed: %w", err)
170 }
171 }
172
173 if flagutil.GetStringFlag(cmd.Rags, flagutil.DownlinkRateLimitFlag) != "" {
174 downlinkRateLimit := flagutil.GetStringFlag(cmd.Rags, flagutil.DownlinkRateLimitFlag)
175 config.DownlinkRateLimit = &downlinkRateLimit
176 if err := validateRateLimit(config.DownlinkRateLimit); err != nil && config.DownlinkRateLimit != nil {
177 return fmt.Errorf("downlink validation failed: %w", err)
178 }
179 }
180
181 if flagutil.GetStringFlag(cmd.Rags, flagutil.ClusterLogLevel) != "" {
182 clusterLogLevel := flagutil.GetStringFlag(cmd.Rags, flagutil.ClusterLogLevel)
183 config.ClusterLogLevel = &clusterLogLevel
184 if err := validateLogLevel(config.ClusterLogLevel); err != nil {
185 return fmt.Errorf("cluster Log Level Validation failed: %w", err)
186 }
187 }
188
189 if flagutil.GetIntFlag(cmd.Rags, flagutil.MaximumLanOutageHours) >= 0 {
190 maximumLanOutageHours := flagutil.GetIntFlag(cmd.Rags, flagutil.MaximumLanOutageHours)
191 config.MaximumLanOutageHours = &maximumLanOutageHours
192 if *config.MaximumLanOutageHours < 24 {
193 return fmt.Errorf("maximum LAN outage duration must be at least 24 hours")
194 }
195 }
196
197 reqCtx, cancelReq = context.WithTimeout(context.Background(), time.Duration(30)*time.Second)
198 defer cancelReq()
199
200 return registrar.UpdateClusterConfig(reqCtx, cluster.ClusterEdgeID, config)
201 },
202 }
203 return cmd
204 }
205
206
207
208 func validateLogLevel(level *string) error {
209 if utils.IsNullOrEmpty(level) {
210 return nil
211 }
212 validLogLevels := map[string]bool{
213 "DEBUG": true,
214 "INFO": true,
215 "NOTICE": true,
216 "WARNING": true,
217 "ERROR": true,
218 "CRITICAL": true,
219 "ALERT": true,
220 "EMERGENCY": true,
221 }
222 _, levelExists := validLogLevels[strings.ToUpper(*level)]
223
224 var keys string
225 for i := range validLogLevels {
226 keys = keys + " " + i
227 }
228
229 if !levelExists {
230 return fmt.Errorf("%s is not a valid log level. Please make sure it is one of the following levels: %s", *level, keys)
231 }
232
233 return nil
234 }
235
236 func validateRateLimit(rateLimit *string) error {
237 if rateLimit == nil {
238 return nil
239 }
240
241 rateLimitRegExp, err := regexp.Compile("^[1-9][0-9]*([kK]bit$|[mM]bit$)")
242 if err != nil {
243 return err
244 }
245 if !rateLimitRegExp.MatchString(*rateLimit) {
246 return fmt.Errorf("rate limit does not match expected format in mbit or kbit. Example: 4mbit")
247 }
248 return nil
249 }
250
View as plain text