...

Source file src/edge-infra.dev/pkg/edge/edgecli/commands/networkservice/modify/modify.go

Documentation: edge-infra.dev/pkg/edge/edgecli/commands/networkservice/modify

     1  package modify
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"time"
     8  
     9  	"edge-infra.dev/pkg/edge/api/graph/model"
    10  	"edge-infra.dev/pkg/edge/constants"
    11  	"edge-infra.dev/pkg/edge/edgecli"
    12  	"edge-infra.dev/pkg/edge/edgecli/constructors"
    13  	"edge-infra.dev/pkg/edge/edgecli/flagutil"
    14  	"edge-infra.dev/pkg/lib/cli/command"
    15  	"edge-infra.dev/pkg/lib/cli/rags"
    16  	"edge-infra.dev/pkg/lib/networkvalidator"
    17  )
    18  
    19  var (
    20  	networkAddressValidationMap = map[string]struct {
    21  		validateFunc func(string) bool
    22  		inetType     string
    23  		errorFormat  string
    24  	}{
    25  		flagutil.IPv4Flag: {
    26  			validateFunc: func(ip string) bool {
    27  				return networkvalidator.IsValidDomain(ip) || networkvalidator.ValidateIP(ip)
    28  			},
    29  			inetType:    "inet",
    30  			errorFormat: "invalid IPv4 address or domain %s",
    31  		},
    32  		flagutil.IPv6Flag: {
    33  			validateFunc: func(ip string) bool {
    34  				return networkvalidator.IsValidDomain(ip) || networkvalidator.ValidateIP(ip)
    35  			},
    36  			inetType:    "inet6",
    37  			errorFormat: "invalid IPv6 address or domain %s",
    38  		},
    39  		flagutil.CIDRFlag: {
    40  			validateFunc: networkvalidator.IsValidCIDR,
    41  			inetType:     "inet",
    42  			errorFormat:  "invalid CIDR range %s",
    43  		},
    44  	}
    45  )
    46  
    47  func NewCmd(cfg *edgecli.Config) *command.Command {
    48  	var cmd *command.Command
    49  	flagsets := flagutil.GetConnectionFlags(cfg)
    50  	var storeName, bannerName, networkServiceID string
    51  
    52  	cmd = &command.Command{
    53  		ShortUsage: "edge networkservice modify",
    54  		ShortHelp:  "Modify a Network Service belonging to a Cluster",
    55  		Flags: append(
    56  			flagsets,
    57  			&rags.Rag{
    58  				Name:     flagutil.StoreFlag,
    59  				Usage:    "Name of the Store",
    60  				Value:    &rags.String{Var: &storeName},
    61  				Required: true,
    62  			},
    63  			&rags.Rag{
    64  				Name:     flagutil.BannerFlag,
    65  				Usage:    "Banner name",
    66  				Value:    &rags.String{Var: &bannerName},
    67  				Required: true,
    68  			},
    69  			// At least one of these must be passed. Check later
    70  			&rags.Rag{
    71  				Name:     flagutil.IPv4Flag,
    72  				Usage:    "IPv4 address of service",
    73  				Value:    &rags.String{},
    74  				Required: false,
    75  			},
    76  			&rags.Rag{
    77  				Name:     flagutil.IPv6Flag,
    78  				Usage:    "IPv6 address of service",
    79  				Value:    &rags.String{},
    80  				Required: false,
    81  			},
    82  			&rags.Rag{
    83  				Name:     flagutil.CIDRFlag,
    84  				Usage:    "CIDR range of service",
    85  				Value:    &rags.String{},
    86  				Required: false,
    87  			},
    88  			&rags.Rag{
    89  				Name:     flagutil.NetworkServiceIDFlag,
    90  				Value:    &rags.String{Var: &networkServiceID},
    91  				Usage:    "ID of network service",
    92  				Required: true,
    93  			},
    94  			&rags.Rag{
    95  				Name:     flagutil.Priority,
    96  				Usage:    "Priority of the network service (must be a positive int)",
    97  				Required: false,
    98  				Value:    &rags.Int{Default: 100},
    99  			},
   100  		),
   101  		Exec: func(_ context.Context, _ []string) error {
   102  			if err := flagutil.ValidateRequiredFlags(cmd.Rags); err != nil {
   103  				return err
   104  			}
   105  
   106  			registrar, err := constructors.BuildRegistrar(cmd.Rags)
   107  			if err != nil {
   108  				return err
   109  			}
   110  
   111  			// For modification, you must only provide one or the other, as the DB has no
   112  			// way of representing a single network service as having both an IPv4 and IPv6
   113  			// address (they have to be represented as two separate services).
   114  
   115  			var passedFlags []string
   116  
   117  			// Append IPv4 flag if provided
   118  			if flagutil.GetStringFlag(cmd.Rags, flagutil.IPv4Flag) != "" {
   119  				passedFlags = append(passedFlags, flagutil.IPv4Flag)
   120  			}
   121  
   122  			// Append IPv6 flag if provided
   123  			if flagutil.GetStringFlag(cmd.Rags, flagutil.IPv6Flag) != "" {
   124  				passedFlags = append(passedFlags, flagutil.IPv6Flag)
   125  			}
   126  
   127  			// Append CIDR flag if provided
   128  			if flagutil.GetStringFlag(cmd.Rags, flagutil.CIDRFlag) != "" {
   129  				passedFlags = append(passedFlags, flagutil.CIDRFlag)
   130  			}
   131  
   132  			if len(passedFlags) != 1 {
   133  				return errors.New("must pass exactly one of --ipv4, --ipv6, --cidr")
   134  			}
   135  
   136  			flag := passedFlags[0]
   137  
   138  			networkServiceID := flagutil.GetStringFlag(cmd.Rags, flagutil.NetworkServiceIDFlag)
   139  			storeName := flagutil.GetStringFlag(cmd.Rags, flagutil.StoreFlag)
   140  			bannerName := flagutil.GetStringFlag(cmd.Rags, flagutil.BannerFlag)
   141  
   142  			reqCtx, cancelReq := context.WithTimeout(context.Background(), time.Duration(30)*time.Second)
   143  			defer cancelReq()
   144  
   145  			cluster, err := registrar.GetCluster(reqCtx, storeName, bannerName)
   146  			if err != nil {
   147  				fmt.Println("an error occurred whilst modifying the cluster network service")
   148  				return err
   149  			}
   150  
   151  			var serviceType string
   152  			for _, netService := range cluster.ClusterNetworkServices {
   153  				if netService.NetworkServiceID == networkServiceID {
   154  					serviceType = netService.ServiceType
   155  					break
   156  				}
   157  			}
   158  			if serviceType == "" {
   159  				return errors.New("the networkServiceID provided does not exist in this cluster")
   160  			}
   161  			if serviceType == constants.ServiceTypeClusterDNS {
   162  				return fmt.Errorf("%s is not directly configurable - update %s instead", constants.ServiceTypeClusterDNS, constants.ServiceTypeServiceNetworkCIDR)
   163  			}
   164  
   165  			ipAddress, ipFamily, err := validateNetworkAddress(cmd.Rags, flag, serviceType)
   166  			if err != nil {
   167  				return err
   168  			}
   169  
   170  			netService := model.UpdateNetworkServiceInfo{}
   171  
   172  			if flagutil.GetBoolFlag(cmd.Rags, flagutil.Priority) {
   173  				priority := flagutil.GetIntFlag(cmd.Rags, flagutil.Priority)
   174  				netService.Priority = &priority
   175  			}
   176  			netService.NetworkServiceID = networkServiceID
   177  			netService.IP = ipAddress
   178  			netService.Family = ipFamily
   179  
   180  			// The elements of the slice cannot be pointers, otherwise they will map to a
   181  			// _nullable_ GraphQL type, which isn't what we want.
   182  			networkServices := []model.UpdateNetworkServiceInfo{
   183  				netService,
   184  			}
   185  
   186  			fmt.Println("modifying network services...")
   187  
   188  			reqCtx, cancelReq = context.WithTimeout(context.Background(), time.Duration(30)*time.Second)
   189  			defer cancelReq()
   190  
   191  			if _, err = registrar.ModifyNetworkServices(reqCtx, cluster.ClusterEdgeID, networkServices); err != nil {
   192  				fmt.Println("an error occurred whilst modifying network services")
   193  				return err
   194  			}
   195  
   196  			fmt.Println("network services modified successfully!")
   197  			return nil
   198  		},
   199  	}
   200  	return cmd
   201  }
   202  
   203  func validateNetworkAddress(rs *rags.RagSet, flag string, serviceType string) (string, string, error) {
   204  	switch serviceType {
   205  	case constants.ServiceTypePodNetworkCIDR, constants.ServiceTypeServiceNetworkCIDR:
   206  		if flag == flagutil.IPv4Flag || flag == flagutil.IPv6Flag {
   207  			fmt.Println(flag)
   208  			return "", "", fmt.Errorf("k8s network services must be CIDR ranges - use --cidr not --ipv4/--ipv6")
   209  		}
   210  	default:
   211  		if flag == flagutil.CIDRFlag {
   212  			fmt.Println(flag)
   213  			return "", "", fmt.Errorf("only k8s network services should be CIDR ranges - use --ipv4 or --ipv6, not --cidr")
   214  		}
   215  	}
   216  
   217  	ipAddress := flagutil.GetStringFlag(rs, flag)
   218  
   219  	if validation, ok := networkAddressValidationMap[flag]; ok {
   220  		if !validation.validateFunc(ipAddress) {
   221  			return "", "", fmt.Errorf(validation.errorFormat, ipAddress)
   222  		}
   223  		return ipAddress, validation.inetType, nil
   224  	}
   225  
   226  	return "", "", fmt.Errorf("couldn't validate unknown flag %s", flag)
   227  }
   228  

View as plain text