github.com/verrazzano/verrazzano@v1.7.1/cluster-operator/apis/clusters/v1alpha1/common_webhook.go (about)

     1  // Copyright (c) 2023, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package v1alpha1
     5  
     6  import (
     7  	"context"
     8  	"github.com/verrazzano/verrazzano/cluster-operator/controllers/quickcreate/controller/oci"
     9  	ocnemeta "github.com/verrazzano/verrazzano/cluster-operator/controllers/quickcreate/controller/ocne"
    10  	vzerror "github.com/verrazzano/verrazzano/cluster-operator/internal/errors"
    11  	corev1 "k8s.io/api/core/v1"
    12  	"k8s.io/apimachinery/pkg/runtime"
    13  	"net/url"
    14  	ctrl "sigs.k8s.io/controller-runtime"
    15  	clipkg "sigs.k8s.io/controller-runtime/pkg/client"
    16  	"strings"
    17  )
    18  
    19  type (
    20  	validationContext struct {
    21  		Ctx               context.Context
    22  		Cli               clipkg.Client
    23  		OCIClientGetter   func(creds *oci.Credentials) (oci.Client, error)
    24  		CredentialsLoader oci.CredentialsLoader
    25  		Errors            *vzerror.ErrorAggregator
    26  	}
    27  )
    28  
    29  var (
    30  	NewValidationContext = newValidationContext
    31  )
    32  
    33  func newValidationContext() (*validationContext, error) {
    34  	cli, err := getWebhookClient()
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	return &validationContext{
    39  		Ctx:               context.Background(),
    40  		Cli:               cli,
    41  		CredentialsLoader: oci.CredentialsLoaderImpl{},
    42  		OCIClientGetter:   oci.NewClient,
    43  		Errors:            vzerror.NewAggregator("\n"),
    44  	}, nil
    45  }
    46  
    47  func getWebhookClient() (clipkg.Client, error) {
    48  	scheme := runtime.NewScheme()
    49  	_ = corev1.AddToScheme(scheme)
    50  	config, err := ctrl.GetConfig()
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	return clipkg.New(config, clipkg.Options{Scheme: scheme})
    55  }
    56  
    57  func addOCINodeErrors(ctx *validationContext, n OCINode, field string) {
    58  	if n.Shape == nil {
    59  		ctx.Errors.Addf("%s.shape is required", field)
    60  	} else if !strings.Contains(*n.Shape, "Flex") {
    61  		if n.OCPUs != nil {
    62  			ctx.Errors.Addf("%s.ocpus should only be specified when using flex shapes", field)
    63  		}
    64  		if n.MemoryGbs != nil {
    65  			ctx.Errors.Addf("%s.memoryGbs should only be specified when using flex shapes", field)
    66  		}
    67  	}
    68  }
    69  
    70  func addOCINetworkErrors(ctx *validationContext, ociClient oci.Client, network *Network, countSubnetRoles int, field string) {
    71  	if network == nil {
    72  		return
    73  	}
    74  	// If creating a new VCN, pre-existing VCN and subnet information should not be specified
    75  	if network.CreateVCN {
    76  		if len(network.VCN) > 0 {
    77  			ctx.Errors.Addf("%s.vcn should not be specified when creating a new VCN", field)
    78  		}
    79  		if len(network.Subnets) > 0 {
    80  			ctx.Errors.Addf("%s.subnets should not be specified when creating a new VCN", field)
    81  		}
    82  	} else { // If using an existing VCN and subnets, validate that these resources are accessible using the provided credentials.
    83  		if len(network.Subnets) != countSubnetRoles {
    84  			ctx.Errors.Addf("%s.subnets should have exactly %d subnets", field, countSubnetRoles)
    85  		}
    86  		if _, err := ociClient.GetVCNByID(ctx.Ctx, network.VCN); err != nil {
    87  			ctx.Errors.Addf("%s.vcn [%s] is not accessible", field, network.VCN)
    88  		}
    89  		subnetCache := map[string]bool{}
    90  		for i, subnet := range network.Subnets {
    91  			if ok := subnetCache[subnet.ID]; ok {
    92  				continue
    93  			}
    94  			ociSubnet, err := ociClient.GetSubnetByID(ctx.Ctx, subnet.ID, string(subnet.Role))
    95  			if err != nil {
    96  				ctx.Errors.Addf("%s.subnets[%d] : [%s] is not accessible", field, i, subnet.ID)
    97  			} else {
    98  				subnetCache[ociSubnet.ID] = true
    99  			}
   100  		}
   101  	}
   102  }
   103  
   104  func addOCNEErrors(ctx *validationContext, ocne OCNE, field string) {
   105  	if _, err := ocnemeta.GetVersionDefaults(ctx.Ctx, ctx.Cli, ocne.Version); err != nil {
   106  		ctx.Errors.Addf("%s.version [%s] is not a known OCNE version", field, ocne.Version)
   107  	}
   108  }
   109  
   110  func addProxyErrors(ctx *validationContext, proxy *Proxy, field string) {
   111  	if proxy == nil {
   112  		return
   113  	}
   114  	if _, err := url.ParseRequestURI(proxy.HTTPSProxy); err != nil {
   115  		ctx.Errors.Addf("%s.httpsProxy is not a valid URL", field)
   116  	}
   117  	if _, err := url.ParseRequestURI(proxy.HTTPProxy); err != nil {
   118  		ctx.Errors.Addf("%s.httpProxy is not a valid URL", field)
   119  	}
   120  }
   121  
   122  func addPrivateRegistryErrors(ctx *validationContext, privateRegistry *PrivateRegistry, field string) {
   123  	if privateRegistry == nil {
   124  		return
   125  	}
   126  	if _, err := url.ParseRequestURI(privateRegistry.URL); err != nil {
   127  		ctx.Errors.Addf("%s.url is not a valid URL", field)
   128  	}
   129  }