github.phpd.cn/cilium/cilium@v1.6.12/pkg/k8s/apis/cilium.io/v2/register.go (about)

     1  // Copyright 2017 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package v2
    16  
    17  import (
    18  	goerrors "errors"
    19  	"fmt"
    20  	"time"
    21  
    22  	k8sconst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
    23  	"github.com/cilium/cilium/pkg/k8s/version"
    24  	"github.com/cilium/cilium/pkg/option"
    25  
    26  	apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
    27  	apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
    28  	"k8s.io/apimachinery/pkg/api/errors"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/runtime"
    31  	"k8s.io/apimachinery/pkg/runtime/schema"
    32  	"k8s.io/apimachinery/pkg/util/wait"
    33  
    34  	goVersion "github.com/hashicorp/go-version"
    35  )
    36  
    37  const (
    38  	// CustomResourceDefinitionGroup is the name of the third party resource group
    39  	CustomResourceDefinitionGroup = k8sconst.GroupName
    40  
    41  	// CustomResourceDefinitionVersion is the current version of the resource
    42  	CustomResourceDefinitionVersion = "v2"
    43  
    44  	// CustomResourceDefinitionSchemaVersion is semver-conformant version of CRD schema
    45  	// Used to determine if CRD needs to be updated in cluster
    46  	CustomResourceDefinitionSchemaVersion = "1.15.1"
    47  
    48  	// CustomResourceDefinitionSchemaVersionKey is key to label which holds the CRD schema version
    49  	CustomResourceDefinitionSchemaVersionKey = "io.cilium.k8s.crd.schema.version"
    50  
    51  	// CNPKindDefinition is the kind name for Cilium Network Policy
    52  	CNPKindDefinition = "CiliumNetworkPolicy"
    53  
    54  	fqdnNameRegex = `^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])\.?$`
    55  
    56  	fqdnPatternRegex = `^(([a-zA-Z0-9\*]|[a-zA-Z0-9\*][a-zA-Z0-9\-\*]*[a-zA-Z0-9\*])\.)*([A-Za-z0-9\*]|[A-Za-z0-9\*][A-Za-z0-9\-\*]*[A-Za-z0-9\*])\.?$`
    57  )
    58  
    59  // SchemeGroupVersion is group version used to register these objects
    60  var SchemeGroupVersion = schema.GroupVersion{
    61  	Group:   CustomResourceDefinitionGroup,
    62  	Version: CustomResourceDefinitionVersion,
    63  }
    64  
    65  // Resource takes an unqualified resource and returns a Group qualified GroupResource
    66  func Resource(resource string) schema.GroupResource {
    67  	return SchemeGroupVersion.WithResource(resource).GroupResource()
    68  }
    69  
    70  var (
    71  	// SchemeBuilder is needed by DeepCopy generator.
    72  	SchemeBuilder runtime.SchemeBuilder
    73  	// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
    74  	localSchemeBuilder = &SchemeBuilder
    75  
    76  	// AddToScheme adds all types of this clientset into the given scheme.
    77  	// This allows composition of clientsets, like in:
    78  	//
    79  	//   import (
    80  	//     "k8s.io/client-go/kubernetes"
    81  	//     clientsetscheme "k8s.io/client-go/kuberentes/scheme"
    82  	//     aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
    83  	//   )
    84  	//
    85  	//   kclientset, _ := kubernetes.NewForConfig(c)
    86  	//   aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
    87  	AddToScheme = localSchemeBuilder.AddToScheme
    88  
    89  	comparableCRDSchemaVersion *goVersion.Version
    90  )
    91  
    92  func init() {
    93  	comparableCRDSchemaVersion = goVersion.Must(
    94  		goVersion.NewVersion(CustomResourceDefinitionSchemaVersion))
    95  
    96  	// We only register manually written functions here. The registration of the
    97  	// generated functions takes place in the generated files. The separation
    98  	// makes the code compile even when the generated files are missing.
    99  	localSchemeBuilder.Register(addKnownTypes)
   100  }
   101  
   102  // Adds the list of known types to api.Scheme.
   103  func addKnownTypes(scheme *runtime.Scheme) error {
   104  	scheme.AddKnownTypes(SchemeGroupVersion,
   105  		&CiliumNetworkPolicy{},
   106  		&CiliumNetworkPolicyList{},
   107  		&CiliumEndpoint{},
   108  		&CiliumEndpointList{},
   109  		&CiliumNode{},
   110  		&CiliumNodeList{},
   111  		&CiliumIdentity{},
   112  		&CiliumIdentityList{},
   113  	)
   114  
   115  	metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
   116  	return nil
   117  }
   118  
   119  // CreateCustomResourceDefinitions creates our CRD objects in the kubernetes
   120  // cluster
   121  func CreateCustomResourceDefinitions(clientset apiextensionsclient.Interface) error {
   122  	if err := createCNPCRD(clientset); err != nil {
   123  		return err
   124  	}
   125  
   126  	if err := createCEPCRD(clientset); err != nil {
   127  		return err
   128  	}
   129  
   130  	if err := createNodeCRD(clientset); err != nil {
   131  		return err
   132  	}
   133  
   134  	if option.Config.IdentityAllocationMode == option.IdentityAllocationModeCRD {
   135  		if err := createIdentityCRD(clientset); err != nil {
   136  			return err
   137  		}
   138  	}
   139  
   140  	return nil
   141  }
   142  
   143  // createCNPCRD creates and updates the CiliumNetworkPolicies CRD. It should be called
   144  // on agent startup but is idempotent and safe to call again.
   145  func createCNPCRD(clientset apiextensionsclient.Interface) error {
   146  	var (
   147  		// CustomResourceDefinitionSingularName is the singular name of custom resource definition
   148  		CustomResourceDefinitionSingularName = "ciliumnetworkpolicy"
   149  
   150  		// CustomResourceDefinitionPluralName is the plural name of custom resource definition
   151  		CustomResourceDefinitionPluralName = "ciliumnetworkpolicies"
   152  
   153  		// CustomResourceDefinitionShortNames are the abbreviated names to refer to this CRD's instances
   154  		CustomResourceDefinitionShortNames = []string{"cnp", "ciliumnp"}
   155  
   156  		// CustomResourceDefinitionKind is the Kind name of custom resource definition
   157  		CustomResourceDefinitionKind = CNPKindDefinition
   158  
   159  		CRDName = CustomResourceDefinitionPluralName + "." + SchemeGroupVersion.Group
   160  	)
   161  
   162  	res := &apiextensionsv1beta1.CustomResourceDefinition{
   163  		ObjectMeta: metav1.ObjectMeta{
   164  			Name: CRDName,
   165  			Labels: map[string]string{
   166  				CustomResourceDefinitionSchemaVersionKey: CustomResourceDefinitionSchemaVersion,
   167  			},
   168  		},
   169  		Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
   170  			Group:   SchemeGroupVersion.Group,
   171  			Version: SchemeGroupVersion.Version,
   172  			Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
   173  				Plural:     CustomResourceDefinitionPluralName,
   174  				Singular:   CustomResourceDefinitionSingularName,
   175  				ShortNames: CustomResourceDefinitionShortNames,
   176  				Kind:       CustomResourceDefinitionKind,
   177  			},
   178  			Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
   179  				Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
   180  			},
   181  			Scope:      apiextensionsv1beta1.NamespaceScoped,
   182  			Validation: &CNPCRV,
   183  		},
   184  	}
   185  	// Kubernetes < 1.12 does not support having the field Type set in the root
   186  	// schema so we need to set it to empty if kube-apiserver does not supports
   187  	// it.
   188  	if !version.Capabilities().FieldTypeInCRDSchema {
   189  		res.Spec.Validation.OpenAPIV3Schema.Type = ""
   190  	}
   191  
   192  	return createUpdateCRD(clientset, "CiliumNetworkPolicy/v2", res)
   193  }
   194  
   195  // createCEPCRD creates and updates the CiliumEndpoint CRD. It should be called
   196  // on agent startup but is idempotent and safe to call again.
   197  func createCEPCRD(clientset apiextensionsclient.Interface) error {
   198  	var (
   199  		// CustomResourceDefinitionSingularName is the singular name of custom resource definition
   200  		CustomResourceDefinitionSingularName = "ciliumendpoint"
   201  
   202  		// CustomResourceDefinitionPluralName is the plural name of custom resource definition
   203  		CustomResourceDefinitionPluralName = "ciliumendpoints"
   204  
   205  		// CustomResourceDefinitionShortNames are the abbreviated names to refer to this CRD's instances
   206  		CustomResourceDefinitionShortNames = []string{"cep", "ciliumep"}
   207  
   208  		// CustomResourceDefinitionKind is the Kind name of custom resource definition
   209  		CustomResourceDefinitionKind = "CiliumEndpoint"
   210  
   211  		CRDName = CustomResourceDefinitionPluralName + "." + SchemeGroupVersion.Group
   212  	)
   213  
   214  	res := &apiextensionsv1beta1.CustomResourceDefinition{
   215  		ObjectMeta: metav1.ObjectMeta{
   216  			Name: CRDName,
   217  		},
   218  		Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
   219  			Group:   SchemeGroupVersion.Group,
   220  			Version: SchemeGroupVersion.Version,
   221  			Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
   222  				Plural:     CustomResourceDefinitionPluralName,
   223  				Singular:   CustomResourceDefinitionSingularName,
   224  				ShortNames: CustomResourceDefinitionShortNames,
   225  				Kind:       CustomResourceDefinitionKind,
   226  			},
   227  			AdditionalPrinterColumns: []apiextensionsv1beta1.CustomResourceColumnDefinition{
   228  				{
   229  					Name:        "Endpoint ID",
   230  					Type:        "integer",
   231  					Description: "Cilium endpoint id",
   232  					JSONPath:    ".status.id",
   233  				},
   234  				{
   235  					Name:        "Identity ID",
   236  					Type:        "integer",
   237  					Description: "Cilium identity id",
   238  					JSONPath:    ".status.identity.id",
   239  				},
   240  				{
   241  					Name:        "Ingress Enforcement",
   242  					Type:        "boolean",
   243  					Description: "Ingress enforcement in the endpoint",
   244  					JSONPath:    ".status.policy.ingress.enforcing",
   245  				},
   246  				{
   247  					Name:        "Egress Enforcement",
   248  					Type:        "boolean",
   249  					Description: "Egress enforcement in the endpoint",
   250  					JSONPath:    ".status.policy.egress.enforcing",
   251  				},
   252  				{
   253  					Name:        "Endpoint State",
   254  					Type:        "string",
   255  					Description: "Endpoint current state",
   256  					JSONPath:    ".status.state",
   257  				},
   258  				{
   259  					Name:        "IPv4",
   260  					Type:        "string",
   261  					Description: "Endpoint IPv4 address",
   262  					JSONPath:    ".status.networking.addressing[0].ipv4",
   263  				},
   264  				{
   265  					Name:        "IPv6",
   266  					Type:        "string",
   267  					Description: "Endpoint IPv6 address",
   268  					JSONPath:    ".status.networking.addressing[0].ipv6",
   269  				},
   270  			},
   271  			Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
   272  				Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
   273  			},
   274  			Scope:      apiextensionsv1beta1.NamespaceScoped,
   275  			Validation: &cepCRV,
   276  		},
   277  	}
   278  
   279  	return createUpdateCRD(clientset, "v2.CiliumEndpoint", res)
   280  }
   281  
   282  // createNodeCRD creates and updates the CiliumNode CRD. It should be called on
   283  // agent startup but is idempotent and safe to call again.
   284  func createNodeCRD(clientset apiextensionsclient.Interface) error {
   285  	res := &apiextensionsv1beta1.CustomResourceDefinition{
   286  		ObjectMeta: metav1.ObjectMeta{
   287  			Name: "ciliumnodes." + SchemeGroupVersion.Group,
   288  		},
   289  		Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
   290  			Group:   SchemeGroupVersion.Group,
   291  			Version: SchemeGroupVersion.Version,
   292  			Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
   293  				Plural:     "ciliumnodes",
   294  				Singular:   "ciliumnode",
   295  				ShortNames: []string{"cn"},
   296  				Kind:       "CiliumNode",
   297  			},
   298  			Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
   299  				Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
   300  			},
   301  			Scope: apiextensionsv1beta1.ClusterScoped,
   302  		},
   303  	}
   304  
   305  	return createUpdateCRD(clientset, "v2.CiliumNode", res)
   306  }
   307  
   308  // createIdentityCRD creates and updates the CiliumIdentity CRD. It should be
   309  // called on agent startup but is idempotent and safe to call again.
   310  func createIdentityCRD(clientset apiextensionsclient.Interface) error {
   311  
   312  	var (
   313  		// CustomResourceDefinitionSingularName is the singular name of custom resource definition
   314  		CustomResourceDefinitionSingularName = "ciliumidentity"
   315  
   316  		// CustomResourceDefinitionPluralName is the plural name of custom resource definition
   317  		CustomResourceDefinitionPluralName = "ciliumidentities"
   318  
   319  		// CustomResourceDefinitionShortNames are the abbreviated names to refer to this CRD's instances
   320  		CustomResourceDefinitionShortNames = []string{"ciliumid"}
   321  
   322  		// CustomResourceDefinitionKind is the Kind name of custom resource definition
   323  		CustomResourceDefinitionKind = "CiliumIdentity"
   324  
   325  		CRDName = CustomResourceDefinitionPluralName + "." + SchemeGroupVersion.Group
   326  	)
   327  
   328  	res := &apiextensionsv1beta1.CustomResourceDefinition{
   329  		ObjectMeta: metav1.ObjectMeta{
   330  			Name: CRDName,
   331  		},
   332  		Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
   333  			Group:   SchemeGroupVersion.Group,
   334  			Version: SchemeGroupVersion.Version,
   335  			Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
   336  				Plural:     CustomResourceDefinitionPluralName,
   337  				Singular:   CustomResourceDefinitionSingularName,
   338  				ShortNames: CustomResourceDefinitionShortNames,
   339  				Kind:       CustomResourceDefinitionKind,
   340  			},
   341  			Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
   342  				Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
   343  			},
   344  			Scope: apiextensionsv1beta1.ClusterScoped,
   345  		},
   346  	}
   347  
   348  	return createUpdateCRD(clientset, "v2.CiliumIdentity", res)
   349  }
   350  
   351  // createUpdateCRD ensures the CRD object is installed into the k8s cluster. It
   352  // will create or update the CRD and it's validation when needed
   353  func createUpdateCRD(clientset apiextensionsclient.Interface, CRDName string, crd *apiextensionsv1beta1.CustomResourceDefinition) error {
   354  	scopedLog := log.WithField("name", CRDName)
   355  
   356  	clusterCRD, err := clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crd.ObjectMeta.Name, metav1.GetOptions{})
   357  	if errors.IsNotFound(err) {
   358  		scopedLog.Info("Creating CRD (CustomResourceDefinition)...")
   359  		clusterCRD, err = clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd)
   360  		// This occurs when multiple agents race to create the CRD. Since another has
   361  		// created it, it will also update it, hence the non-error return.
   362  		if errors.IsAlreadyExists(err) {
   363  			return nil
   364  		}
   365  	}
   366  	if err != nil {
   367  		return err
   368  	}
   369  
   370  	scopedLog.Debug("Checking if CRD (CustomResourceDefinition) needs update...")
   371  	if needsUpdate(clusterCRD) {
   372  		scopedLog.Info("Updating CRD (CustomResourceDefinition)...")
   373  		// Update the CRD with the validation schema.
   374  		err = wait.Poll(500*time.Millisecond, 60*time.Second, func() (bool, error) {
   375  			clusterCRD, err = clientset.ApiextensionsV1beta1().
   376  				CustomResourceDefinitions().Get(crd.ObjectMeta.Name, metav1.GetOptions{})
   377  
   378  			if err != nil {
   379  				return false, err
   380  			}
   381  
   382  			// This seems too permissive but we only get here if the version is
   383  			// different per needsUpdate above. If so, we want to update on any
   384  			// validation change including adding or removing validation.
   385  			if needsUpdate(clusterCRD) {
   386  				scopedLog.Debug("CRD validation is different, updating it...")
   387  				clusterCRD.ObjectMeta.Labels = crd.ObjectMeta.Labels
   388  				clusterCRD.Spec = crd.Spec
   389  				_, err = clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Update(clusterCRD)
   390  				if err == nil {
   391  					return true, nil
   392  				}
   393  				scopedLog.WithError(err).Debug("Unable to update CRD validation")
   394  				return false, err
   395  			}
   396  
   397  			return true, nil
   398  		})
   399  		if err != nil {
   400  			scopedLog.WithError(err).Error("Unable to update CRD")
   401  			return err
   402  		}
   403  	}
   404  
   405  	// wait for the CRD to be established
   406  	scopedLog.Debug("Waiting for CRD (CustomResourceDefinition) to be available...")
   407  	err = wait.Poll(500*time.Millisecond, 60*time.Second, func() (bool, error) {
   408  		crd, err := clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crd.ObjectMeta.Name, metav1.GetOptions{})
   409  		if err != nil {
   410  			return false, err
   411  		}
   412  		for _, cond := range crd.Status.Conditions {
   413  			switch cond.Type {
   414  			case apiextensionsv1beta1.Established:
   415  				if cond.Status == apiextensionsv1beta1.ConditionTrue {
   416  					return true, err
   417  				}
   418  			case apiextensionsv1beta1.NamesAccepted:
   419  				if cond.Status == apiextensionsv1beta1.ConditionFalse {
   420  					scopedLog.WithError(goerrors.New(cond.Reason)).Error("Name conflict for CRD")
   421  					return false, err
   422  				}
   423  			}
   424  		}
   425  		return false, err
   426  	})
   427  	if err != nil {
   428  		deleteErr := clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Delete(crd.ObjectMeta.Name, nil)
   429  		if deleteErr != nil {
   430  			return fmt.Errorf("unable to delete k8s %s CRD %s. Deleting CRD due: %s", CRDName, deleteErr, err)
   431  		}
   432  		return err
   433  	}
   434  
   435  	scopedLog.Info("CRD (CustomResourceDefinition) is installed and up-to-date")
   436  	return nil
   437  }
   438  
   439  func needsUpdate(clusterCRD *apiextensionsv1beta1.CustomResourceDefinition) bool {
   440  
   441  	if clusterCRD.Spec.Validation == nil {
   442  		// no validation detected
   443  		return true
   444  	}
   445  	v, ok := clusterCRD.Labels[CustomResourceDefinitionSchemaVersionKey]
   446  	if !ok {
   447  		// no schema version detected
   448  		return true
   449  	}
   450  	clusterVersion, err := goVersion.NewVersion(v)
   451  	if err != nil || clusterVersion.LessThan(comparableCRDSchemaVersion) {
   452  		// version in cluster is either unparsable or smaller than current version
   453  		return true
   454  	}
   455  	return false
   456  }
   457  
   458  func getStr(str string) *string {
   459  	return &str
   460  }
   461  
   462  func getInt64(i int64) *int64 {
   463  	return &i
   464  }
   465  
   466  var (
   467  	// cepCRV is a minimal validation for CEP objects. Since only the agent is
   468  	// creating them, it is better to be permissive and have some data, if buggy,
   469  	// than to have no data in k8s.
   470  	cepCRV = apiextensionsv1beta1.CustomResourceValidation{
   471  		OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{},
   472  	}
   473  
   474  	CNPCRV = apiextensionsv1beta1.CustomResourceValidation{
   475  		OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{
   476  			// TODO: remove the following comment when we add checker
   477  			// to detect if we should install the CNP validation for k8s > 1.11
   478  			// with this line uncommented.
   479  			Type:       "object",
   480  			Properties: properties,
   481  		},
   482  	}
   483  
   484  	properties = map[string]apiextensionsv1beta1.JSONSchemaProps{
   485  		"CIDR":                     CIDR,
   486  		"CIDRRule":                 CIDRRule,
   487  		"EgressRule":               EgressRule,
   488  		"EndpointSelector":         EndpointSelector,
   489  		"IngressRule":              IngressRule,
   490  		"K8sServiceNamespace":      K8sServiceNamespace,
   491  		"L7Rules":                  L7Rules,
   492  		"Label":                    Label,
   493  		"LabelSelector":            LabelSelector,
   494  		"LabelSelectorRequirement": LabelSelectorRequirement,
   495  		"PortProtocol":             PortProtocol,
   496  		"PortRule":                 PortRule,
   497  		"PortRuleHTTP":             PortRuleHTTP,
   498  		"PortRuleKafka":            PortRuleKafka,
   499  		"PortRuleL7":               PortRuleL7,
   500  		"Rule":                     Rule,
   501  		"Service":                  Service,
   502  		"ServiceSelector":          ServiceSelector,
   503  		"spec":                     spec,
   504  		"specs":                    specs,
   505  	}
   506  
   507  	CIDR = apiextensionsv1beta1.JSONSchemaProps{
   508  		Description: "CIDR is a CIDR prefix / IP Block.",
   509  		Type:        "string",
   510  		OneOf: []apiextensionsv1beta1.JSONSchemaProps{
   511  			{
   512  				// IPv4 CIDR
   513  				Pattern: `^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4]` +
   514  					`[0-9]|[01]?[0-9][0-9]?)\/([0-9]|[1-2][0-9]|3[0-2])$`,
   515  			},
   516  			{
   517  				// IPv6 CIDR
   518  				Pattern: `^s*((([0-9A-Fa-f]{1,4}:){7}(:|([0-9A-Fa-f]{1,4})))` +
   519  					`|(([0-9A-Fa-f]{1,4}:){6}:([0-9A-Fa-f]{1,4})?)` +
   520  					`|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){0,1}):([0-9A-Fa-f]{1,4})?))` +
   521  					`|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){0,2}):([0-9A-Fa-f]{1,4})?))` +
   522  					`|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){0,3}):([0-9A-Fa-f]{1,4})?))` +
   523  					`|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){0,4}):([0-9A-Fa-f]{1,4})?))` +
   524  					`|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){0,5}):([0-9A-Fa-f]{1,4})?))` +
   525  					`|(:(:|((:[0-9A-Fa-f]{1,4}){1,7}))))` +
   526  					`(%.+)?s*/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])$`,
   527  			},
   528  		},
   529  	}
   530  
   531  	CIDRRule = apiextensionsv1beta1.JSONSchemaProps{
   532  		Type: "object",
   533  		Description: "CIDRRule is a rule that specifies a CIDR prefix to/from which outside " +
   534  			"communication is allowed, along with an optional list of subnets within that CIDR " +
   535  			"prefix to/from which outside communication is not allowed.",
   536  		Required: []string{
   537  			"cidr",
   538  		},
   539  		Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
   540  			"cidr": CIDR,
   541  			"except": {
   542  				Description: "ExceptCIDRs is a list of IP blocks which the endpoint subject to " +
   543  					"the rule is not allowed to initiate connections to. These CIDR prefixes " +
   544  					"should be contained within Cidr. These exceptions are only applied to the " +
   545  					"Cidr in this CIDRRule, and do not apply to any other CIDR prefixes in any " +
   546  					"other CIDRRules.",
   547  				Type: "array",
   548  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   549  					Schema: &CIDR,
   550  				},
   551  			},
   552  		},
   553  	}
   554  
   555  	EgressRule = apiextensionsv1beta1.JSONSchemaProps{
   556  		Type: "object",
   557  		Description: "EgressRule contains all rule types which can be applied at egress, i.e. " +
   558  			"network traffic that originates inside the endpoint and exits the endpoint " +
   559  			"selected by the endpointSelector.\n\n- All members of this structure are optional. " +
   560  			"If omitted or empty, the\n  member will have no effect on the rule.\n\n- For now, " +
   561  			"combining ToPorts and ToCIDR in the same rule is not supported\n  and such rules " +
   562  			"will be rejected. In the future, this will be supported and\n  if if multiple " +
   563  			"members of the structure are specified, then all members\n  must match in order " +
   564  			"for the rule to take effect.",
   565  		Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
   566  			"toCIDR": {
   567  				Description: "ToCIDR is a list of IP blocks which the endpoint subject to the " +
   568  					"rule is allowed to initiate connections. This will match on the " +
   569  					"destination IP address of outgoing connections. Adding a prefix into " +
   570  					"ToCIDR or into ToCIDRSet with no ExcludeCIDRs is equivalent. Overlaps are " +
   571  					"allowed between ToCIDR and ToCIDRSet.\n\nExample: Any endpoint with the " +
   572  					"label \"app=database-proxy\" is allowed to initiate connections to " +
   573  					"10.2.3.0/24",
   574  				Type: "array",
   575  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   576  					Schema: &CIDR,
   577  				},
   578  			},
   579  			"toCIDRSet": {
   580  				Description: "ToCIDRSet is a list of IP blocks which the endpoint subject to " +
   581  					"the rule is allowed to initiate connections to in addition to connections " +
   582  					"which are allowed via FromEndpoints, along with a list of subnets " +
   583  					"contained within their corresponding IP block to which traffic should not " +
   584  					"be allowed. This will match on the destination IP address of outgoing " +
   585  					"connections. Adding a prefix into ToCIDR or into ToCIDRSet with no " +
   586  					"ExcludeCIDRs is equivalent. Overlaps are allowed between ToCIDR and " +
   587  					"ToCIDRSet.\n\nExample: Any endpoint with the label \"app=database-proxy\" " +
   588  					"is allowed to initiate connections to 10.2.3.0/24 except from IPs in " +
   589  					"subnet 10.2.3.0/28.",
   590  				Type: "array",
   591  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   592  					Schema: &CIDRRule,
   593  				},
   594  			},
   595  			"toEntities": {
   596  				Description: "ToEntities is a list of special entities to which the endpoint " +
   597  					"subject to the rule is allowed to initiate connections. Supported " +
   598  					"entities are `world`, `cluster` and `host`",
   599  				Type: "array",
   600  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   601  					Schema: &apiextensionsv1beta1.JSONSchemaProps{
   602  						Type: "string",
   603  					},
   604  				},
   605  			},
   606  			"toPorts": {
   607  				Description: "ToPorts is a list of destination ports identified by port number " +
   608  					"and protocol which the endpoint subject to the rule is allowed to connect " +
   609  					"to.\n\nExample: Any endpoint with the label \"role=frontend\" is allowed " +
   610  					"to initiate connections to destination port 8080/tcp",
   611  				Type: "array",
   612  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   613  					Schema: &PortRule,
   614  				},
   615  			},
   616  			"toServices": {
   617  				Description: "ToServices is a list of services to which the endpoint subject " +
   618  					"to the rule is allowed to initiate connections.\n\nExample: Any endpoint " +
   619  					"with the label \"app=backend-app\" is allowed to initiate connections to " +
   620  					"all cidrs backing the \"external-service\" service",
   621  				Type: "array",
   622  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   623  					Schema: &Service,
   624  				},
   625  			},
   626  			"toEndpoints": {
   627  				Description: "ToEndpoints is a list of endpoints identified by an " +
   628  					"EndpointSelector to which the endpoint subject to the rule" +
   629  					"is allowed to communicate.\n\nExample: Any endpoint with the label " +
   630  					"\"role=frontend\" can be consumed by any endpoint carrying the label " +
   631  					"\"role=backend\".",
   632  				Type: "array",
   633  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   634  					Schema: &EndpointSelector,
   635  				},
   636  			},
   637  			"toRequires": {
   638  				Description: "ToRequires is a list of additional constraints which must be " +
   639  					"met in order for the selected endpoints to be able to reach other " +
   640  					"endpoints. These additional constraints do not by themselves grant access " +
   641  					"privileges and must always be accompanied with at least one matching " +
   642  					"FromEndpoints.\n\nExample: Any Endpoint with the label \"team=A\" " +
   643  					"requires any endpoint to which it communicates to also carry the label " +
   644  					"\"team=A\".",
   645  				Type: "array",
   646  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   647  					Schema: &EndpointSelector,
   648  				},
   649  			},
   650  			"toGroups": {
   651  				Type: "array",
   652  				Description: `ToGroups is a list of constraints that will
   653  				gather data from third-party providers and create a new
   654  				derived policy.`,
   655  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   656  					Schema: &apiextensionsv1beta1.JSONSchemaProps{
   657  						Type: "object",
   658  						Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
   659  							"aws": AWSGroup,
   660  						},
   661  					},
   662  				},
   663  			},
   664  			"toFQDNs": {
   665  				Description: `ToFQDNs is a list of rules matching fqdns that endpoint
   666  				is allowed to communicate with`,
   667  				Type: "array",
   668  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   669  					Schema: &FQDNRule,
   670  				},
   671  			},
   672  		},
   673  	}
   674  
   675  	FQDNRule = apiextensionsv1beta1.JSONSchemaProps{
   676  		Type:        "object",
   677  		Description: `FQDNRule is a rule that specifies an fully qualified domain name to which outside communication is allowed`,
   678  		Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
   679  			"matchName":    MatchFQDNName,
   680  			"matchPattern": MatchFQDNPattern,
   681  		},
   682  	}
   683  
   684  	MatchFQDNName = apiextensionsv1beta1.JSONSchemaProps{
   685  		Description: `MatchName matches fqdn name`,
   686  		Type:        "string",
   687  		Pattern:     fqdnNameRegex,
   688  	}
   689  
   690  	MatchFQDNPattern = apiextensionsv1beta1.JSONSchemaProps{
   691  		Description: `MatchPattern matches fqdn by pattern`,
   692  		Type:        "string",
   693  		Pattern:     fqdnPatternRegex,
   694  	}
   695  
   696  	AWSGroup = apiextensionsv1beta1.JSONSchemaProps{
   697  		Type: "object",
   698  		Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
   699  			"securityGroupsIds": {
   700  				Description: `SecurityGroupsIds is the list of AWS security
   701  				group IDs that will filter the instances IPs from the AWS API`,
   702  				Type: "array",
   703  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   704  					Schema: &apiextensionsv1beta1.JSONSchemaProps{
   705  						Type: "string",
   706  					},
   707  				},
   708  			},
   709  			"securityGroupsNames": {
   710  				Description: `SecurityGroupsNames is the list of  AWS security
   711  				group names that will filter the instances IPs from the AWS API`,
   712  				Type: "array",
   713  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   714  					Schema: &apiextensionsv1beta1.JSONSchemaProps{
   715  						Type: "string",
   716  					},
   717  				},
   718  			},
   719  			"region": {
   720  				Description: `Region is the key that will filter the AWS EC2
   721  				instances in the given region`,
   722  				Type: "string",
   723  			},
   724  		},
   725  	}
   726  	EndpointSelector = initEndpointSelector()
   727  
   728  	IngressRule = apiextensionsv1beta1.JSONSchemaProps{
   729  		Type: "object",
   730  		Description: "IngressRule contains all rule types which can be applied at ingress, " +
   731  			"i.e. network traffic that originates outside of the endpoint and is entering " +
   732  			"the endpoint selected by the endpointSelector.\n\n- All members of this structure " +
   733  			"are optional. If omitted or empty, the\n  member will have no effect on the rule." +
   734  			"\n\n- If multiple members are set, all of them need to match in order for\n  " +
   735  			"the rule to take effect. The exception to this rule is FromRequires field;\n  " +
   736  			"the effects of any Requires field in any rule will apply to all other\n  rules " +
   737  			"as well.\n\n- For now, combining ToPorts, FromCIDR, and FromEndpoints in the same " +
   738  			"rule\n  is not supported and any such rules will be rejected. In the future, " +
   739  			"this\n  will be supported and if multiple members of this structure are specified," +
   740  			"\n then all members must match in order for the rule to take effect. The\n  " +
   741  			"exception to this rule is the Requires field, the effects of any Requires\n  " +
   742  			"field in any rule will apply to all other rules as well.",
   743  		Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
   744  			"fromCIDR": {
   745  				Description: "FromCIDR is a list of IP blocks which the endpoint subject to " +
   746  					"the rule is allowed to receive connections from. This will match on the " +
   747  					"source IP address of incoming connections. Adding  a prefix into FromCIDR " +
   748  					"or into FromCIDRSet with no ExcludeCIDRs is  equivalent. Overlaps are " +
   749  					"allowed between FromCIDR and FromCIDRSet.\n\nExample: Any endpoint with " +
   750  					"the label \"app=my-legacy-pet\" is allowed to receive connections from " +
   751  					"10.3.9.1",
   752  				Type: "array",
   753  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   754  					Schema: &CIDR,
   755  				},
   756  			},
   757  			"fromCIDRSet": {
   758  				Description: "FromCIDRSet is a list of IP blocks which the endpoint subject to " +
   759  					"the rule is allowed to receive connections from in addition to " +
   760  					"FromEndpoints, along with a list of subnets contained within their " +
   761  					"corresponding IP block from which traffic should not be allowed. This " +
   762  					"will match on the source IP address of incoming connections. Adding a " +
   763  					"prefix into FromCIDR or into FromCIDRSet with no ExcludeCIDRs is " +
   764  					"equivalent. Overlaps are allowed between FromCIDR and FromCIDRSet." +
   765  					"\n\nExample: Any endpoint with the label \"app=my-legacy-pet\" is allowed " +
   766  					"to receive connections from 10.0.0.0/8 except from IPs in subnet " +
   767  					"10.96.0.0/12.",
   768  				Type: "array",
   769  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   770  					Schema: &CIDRRule,
   771  				},
   772  			},
   773  			"fromEndpoints": {
   774  				Description: "FromEndpoints is a list of endpoints identified by an " +
   775  					"EndpointSelector which are allowed to communicate with the endpoint " +
   776  					"subject to the rule.\n\nExample: Any endpoint with the label " +
   777  					"\"role=backend\" can be consumed by any endpoint carrying the label " +
   778  					"\"role=frontend\".",
   779  				Type: "array",
   780  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   781  					Schema: &EndpointSelector,
   782  				},
   783  			},
   784  			"fromEntities": {
   785  				Description: "FromEntities is a list of special entities which the endpoint " +
   786  					"subject to the rule is allowed to receive connections from. Supported " +
   787  					"entities are `world`, `cluster`, `host`, and `init`",
   788  				Type: "array",
   789  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   790  					Schema: &apiextensionsv1beta1.JSONSchemaProps{
   791  						Type: "string",
   792  					},
   793  				},
   794  			},
   795  			"fromRequires": {
   796  				Description: "FromRequires is a list of additional constraints which must be " +
   797  					"met in order for the selected endpoints to be reachable. These additional " +
   798  					"constraints do no by itself grant access privileges and must always be " +
   799  					"accompanied with at least one matching FromEndpoints.\n\nExample: Any " +
   800  					"Endpoint with the label \"team=A\" requires consuming endpoint to also " +
   801  					"carry the label \"team=A\".",
   802  				Type: "array",
   803  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   804  					Schema: &EndpointSelector,
   805  				},
   806  			},
   807  			"toPorts": {
   808  				Description: "ToPorts is a list of destination ports identified by port number " +
   809  					"and protocol which the endpoint subject to the rule is allowed to receive " +
   810  					"connections on.\n\nExample: Any endpoint with the label \"app=httpd\" can " +
   811  					"only accept incoming connections on port 80/tcp.",
   812  				Type: "array",
   813  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   814  					Schema: &PortRule,
   815  				},
   816  			},
   817  		},
   818  	}
   819  
   820  	K8sServiceNamespace = apiextensionsv1beta1.JSONSchemaProps{
   821  		Type: "object",
   822  		Description: "K8sServiceNamespace is an abstraction for the k8s service + namespace " +
   823  			"types.",
   824  		Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
   825  			"namespace": {
   826  				Type: "string",
   827  			},
   828  			"serviceName": {
   829  				Type: "string",
   830  			},
   831  		},
   832  	}
   833  
   834  	L7Rules = apiextensionsv1beta1.JSONSchemaProps{
   835  		Type: "object",
   836  		Description: "L7Rules is a union of port level rule types. Mixing of different port " +
   837  			"level rule types is disallowed, so exactly one of the following must be set. If " +
   838  			"none are specified, then no additional port level rules are applied.",
   839  		Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
   840  			"http": {
   841  				Description: "HTTP specific rules.",
   842  				Type:        "array",
   843  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   844  					Schema: &PortRuleHTTP,
   845  				},
   846  			},
   847  			"kafka": {
   848  				Description: "Kafka-specific rules.",
   849  				Type:        "array",
   850  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   851  					Schema: &PortRuleKafka,
   852  				},
   853  			},
   854  			"l7proto": {
   855  				Description: "Parser type name that uses Key-Value pair rules.",
   856  				Type:        "string",
   857  			},
   858  			"l7": {
   859  				Description: "Generic Key-Value pair rules.",
   860  				Type:        "array",
   861  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   862  					Schema: &PortRuleL7,
   863  				},
   864  			},
   865  			"dns": {
   866  				Description: "DNS specific rules",
   867  				Type:        "array",
   868  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   869  					Schema: &PortRuleDNS,
   870  				},
   871  			},
   872  		},
   873  	}
   874  
   875  	PortRuleDNS = apiextensionsv1beta1.JSONSchemaProps{
   876  		Type:        "object",
   877  		Description: `FQDNRule is a rule that specifies an fully qualified domain name to which outside communication is allowed`,
   878  		Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
   879  			"matchName":    MatchFQDNName,
   880  			"matchPattern": MatchFQDNPattern,
   881  		},
   882  	}
   883  
   884  	Label = apiextensionsv1beta1.JSONSchemaProps{
   885  		Type:        "object",
   886  		Description: "Label is the cilium's representation of a container label.",
   887  		Required: []string{
   888  			"key",
   889  		},
   890  		Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
   891  			"key": {
   892  				Type: "string",
   893  			},
   894  			"source": {
   895  				Description: "Source can be one of the values present in const.go " +
   896  					"(e.g.: LabelSourceContainer)",
   897  				Type: "string",
   898  			},
   899  			"value": {
   900  				Type: "string",
   901  			},
   902  		},
   903  	}
   904  
   905  	LabelSelector = apiextensionsv1beta1.JSONSchemaProps{
   906  		Type: "object",
   907  		Description: "A label selector is a label query over a set of resources. The result " +
   908  			"of matchLabels and matchExpressions are ANDed. An empty label selector matches " +
   909  			"all objects. A null label selector matches no objects.",
   910  		Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
   911  			"matchLabels": {
   912  				Description: "matchLabels is a map of {key,value} pairs. A single {key,value} " +
   913  					"in the matchLabels map is equivalent to an element of matchExpressions, " +
   914  					"whose key field is \"key\", the operator is \"In\", and the values array " +
   915  					"contains only \"value\". The requirements are ANDed.",
   916  				Type: "object",
   917  			},
   918  			"matchExpressions": {
   919  				Description: "matchExpressions is a list of label selector requirements. " +
   920  					"The requirements are ANDed.",
   921  				Type: "array",
   922  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   923  					Schema: &LabelSelectorRequirement,
   924  				},
   925  			},
   926  		},
   927  	}
   928  
   929  	LabelSelectorRequirement = apiextensionsv1beta1.JSONSchemaProps{
   930  		Type: "object",
   931  		Description: "A label selector requirement is a selector that contains values, a key, " +
   932  			"and an operator that relates the key and values.",
   933  		Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
   934  			"key": {
   935  				Description: "key is the label key that the selector applies to.",
   936  				Type:        "string",
   937  			},
   938  			"operator": {
   939  				Description: "operator represents a key's relationship to a set of values. " +
   940  					"Valid operators are In, NotIn, Exists and DoesNotExist.",
   941  				Type: "string",
   942  				Enum: []apiextensionsv1beta1.JSON{
   943  					{
   944  						Raw: []byte(`"In"`),
   945  					},
   946  					{
   947  						Raw: []byte(`"NotIn"`),
   948  					},
   949  					{
   950  						Raw: []byte(`"Exists"`),
   951  					},
   952  					{
   953  						Raw: []byte(`"DoesNotExist"`),
   954  					},
   955  				},
   956  			},
   957  			"values": {
   958  				Description: "values is an array of string values. If the operator is In or " +
   959  					"NotIn, the values array must be non-empty. If the operator is Exists or " +
   960  					"DoesNotExist, the values array must be empty. This array is replaced " +
   961  					"during a strategic merge patch.",
   962  				Type: "array",
   963  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
   964  					Schema: &apiextensionsv1beta1.JSONSchemaProps{
   965  						Type: "string",
   966  					},
   967  				},
   968  			},
   969  		},
   970  		Required: []string{"key", "operator"},
   971  	}
   972  
   973  	PortProtocol = apiextensionsv1beta1.JSONSchemaProps{
   974  		Type:        "object",
   975  		Description: "PortProtocol specifies an L4 port with an optional transport protocol",
   976  		Required: []string{
   977  			"port",
   978  		},
   979  		Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
   980  			"port": {
   981  				Description: "Port is an L4 port number. For now the string will be strictly " +
   982  					"parsed as a single uint16. In the future, this field may support ranges " +
   983  					"in the form \"1024-2048",
   984  				Type: "string",
   985  				// uint16 string regex
   986  				Pattern: `^(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|` +
   987  					`[1-5][0-9]{4}|[0-9]{1,4})$`,
   988  			},
   989  			"protocol": {
   990  				Description: `Protocol is the L4 protocol. If omitted or empty, any protocol ` +
   991  					`matches. Accepted values: "TCP", "UDP", ""/"ANY"\n\nMatching on ` +
   992  					`ICMP is not supported.`,
   993  				Type: "string",
   994  				Enum: []apiextensionsv1beta1.JSON{
   995  					{
   996  						Raw: []byte(`"TCP"`),
   997  					},
   998  					{
   999  						Raw: []byte(`"UDP"`),
  1000  					},
  1001  					{
  1002  						Raw: []byte(`"ANY"`),
  1003  					},
  1004  				},
  1005  			},
  1006  		},
  1007  	}
  1008  
  1009  	PortRule = apiextensionsv1beta1.JSONSchemaProps{
  1010  		Type: "object",
  1011  		Description: "PortRule is a list of ports/protocol combinations with optional Layer 7 " +
  1012  			"rules which must be met.",
  1013  		Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
  1014  			"ports": {
  1015  				Description: "Ports is a list of L4 port/protocol\n\nIf omitted or empty but " +
  1016  					"RedirectPort is set, then all ports of the endpoint subject to either the " +
  1017  					"ingress or egress rule are being redirected.",
  1018  				Type: "array",
  1019  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
  1020  					Schema: &PortProtocol,
  1021  				},
  1022  			},
  1023  			"redirectPort": {
  1024  				Description: "RedirectPort is the L4 port which, if set, all traffic matching " +
  1025  					"the Ports is being redirected to. Whatever listener behind that port " +
  1026  					"becomes responsible to enforce the port rules and is also responsible to " +
  1027  					"reinject all traffic back and ensure it reaches its original destination.",
  1028  				Type:   "integer",
  1029  				Format: "uint16",
  1030  			},
  1031  			"rules": initPortRule(),
  1032  		},
  1033  	}
  1034  
  1035  	PortRuleHTTP = apiextensionsv1beta1.JSONSchemaProps{
  1036  		Type: "object",
  1037  		Description: "PortRuleHTTP is a list of HTTP protocol constraints. All fields are " +
  1038  			"optional, if all fields are empty or missing, the rule does not have any effect." +
  1039  			"\n\nAll fields of this type are extended POSIX regex as defined by " +
  1040  			"IEEE Std 1003.1, (i.e this follows the egrep/unix syntax, not the perl syntax) " +
  1041  			"matched against the path of an incoming request. Currently it can contain " +
  1042  			"characters disallowed from the conventional \"path\" part of a URL as defined by " +
  1043  			"RFC 3986.",
  1044  		Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
  1045  			"headers": {
  1046  				Description: "Headers is a list of HTTP headers which must be present in the " +
  1047  					"request. If omitted or empty, requests are allowed regardless of headers " +
  1048  					"present.",
  1049  				Type: "array",
  1050  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
  1051  					Schema: &apiextensionsv1beta1.JSONSchemaProps{
  1052  						Type: "string",
  1053  					},
  1054  				},
  1055  			},
  1056  			"host": {
  1057  				Description: "Host is an extended POSIX regex matched against the host header " +
  1058  					"of a request, e.g. \"foo.com\"\n\nIf omitted or empty, the value of the " +
  1059  					"host header is ignored.",
  1060  				Type:   "string",
  1061  				Format: "idn-hostname",
  1062  			},
  1063  			"method": {
  1064  				Description: "Method is an extended POSIX regex matched against the method of " +
  1065  					"a request, e.g. \"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", ...\n\n" +
  1066  					"If omitted or empty, all methods are allowed.",
  1067  				Type: "string",
  1068  			},
  1069  			"path": {
  1070  				Description: "Path is an extended POSIX regex matched against the path of a " +
  1071  					"request. Currently it can contain characters disallowed from the " +
  1072  					"conventional \"path\" part of a URL as defined by RFC 3986.\n\n" +
  1073  					"If omitted or empty, all paths are all allowed.",
  1074  				Type: "string",
  1075  			},
  1076  		},
  1077  	}
  1078  
  1079  	PortRuleKafka = apiextensionsv1beta1.JSONSchemaProps{
  1080  		Type: "object",
  1081  		Description: "PortRuleKafka is a list of Kafka protocol constraints. All fields are " +
  1082  			"optional, if all fields are empty or missing, the rule will match all Kafka " +
  1083  			"messages.",
  1084  		Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
  1085  			"role": {
  1086  				Description: "Role is a case-insensitive string and describes a group of API keys" +
  1087  					"necessary to perform certain higher level Kafka operations such as" +
  1088  					"\"produce\" or \"consume\". An APIGroup automatically expands into all APIKeys" +
  1089  					"required to perform the specified higher level operation." +
  1090  					"The following values are supported:" +
  1091  					"- \"produce\": Allow producing to the topics specified in the rule" +
  1092  					"- \"consume\": Allow consuming from the topics specified in the rule" +
  1093  					"This field is incompatible with the APIKey field, either APIKey or Role" +
  1094  					"may be specified. If omitted or empty, the field has no effect and the " +
  1095  					"logic of the APIKey field applies.",
  1096  				Type: "string",
  1097  				Enum: []apiextensionsv1beta1.JSON{
  1098  					{
  1099  						Raw: []byte(`"produce"`),
  1100  					},
  1101  					{
  1102  						Raw: []byte(`"consume"`),
  1103  					},
  1104  				},
  1105  			},
  1106  			"apiKey": {
  1107  				Description: "APIKey is a case-insensitive string matched against the key of " +
  1108  					"a request, e.g. \"produce\", \"fetch\", \"createtopic\", \"deletetopic\", " +
  1109  					"et al Reference: https://kafka.apache.org/protocol#protocol_api_keys\n\n" +
  1110  					"If omitted or empty, all keys are allowed.",
  1111  				Type: "string",
  1112  			},
  1113  			"apiVersion": {
  1114  				Description: "APIVersion is the version matched against the api version of the " +
  1115  					"Kafka message. If set, it has to be a string representing a positive " +
  1116  					"integer.\n\nIf omitted or empty, all versions are allowed.",
  1117  				Type: "string",
  1118  			},
  1119  			"clientID": {
  1120  				Description: "ClientID is the client identifier as provided in the request.\n\n" +
  1121  					"From Kafka protocol documentation: This is a user supplied identifier for " +
  1122  					"the client application. The user can use any identifier they like and it " +
  1123  					"will be used when logging errors, monitoring aggregates, etc. For " +
  1124  					"example, one might want to monitor not just the requests per second " +
  1125  					"overall, but the number coming from each client application (each of " +
  1126  					"which could reside on multiple servers). This id acts as a logical " +
  1127  					"grouping across all requests from a particular client.\n\nIf omitted or " +
  1128  					"empty, all client identifiers are allowed.",
  1129  				Type: "string",
  1130  			},
  1131  			"topic": {
  1132  				Description: "Topic is the topic name contained in the message. If a Kafka " +
  1133  					"request contains multiple topics, then all topics must be allowed or the " +
  1134  					"message will be rejected.\n\nThis constraint is ignored if the matched " +
  1135  					"request message type doesn't contain any topic. Maximum size of Topic can " +
  1136  					"be 249 characters as per recent Kafka spec and allowed characters are " +
  1137  					"a-z, A-Z, 0-9, -, . and _ Older Kafka versions had longer topic lengths " +
  1138  					"of 255, but in Kafka 0.10 version the length was changed from 255 to 249. " +
  1139  					"For compatibility reasons we are using 255\n\nIf omitted or empty, all " +
  1140  					"topics are allowed.",
  1141  				Type:      "string",
  1142  				MaxLength: getInt64(255),
  1143  			},
  1144  		},
  1145  	}
  1146  
  1147  	PortRuleL7 = apiextensionsv1beta1.JSONSchemaProps{
  1148  		Type: "object",
  1149  		Description: "PortRuleL7 is a map of {key,value} pairs which is passed to the " +
  1150  			"parser referenced in l7proto. It is up to the parser to define what to " +
  1151  			"do with the map data. If omitted or empty, all requests are allowed. " +
  1152  			"Both keys and values must be strings.",
  1153  		//
  1154  		// AdditionalProperties is supported by k8s 1.11 and later only
  1155  		// Without it non-string value types are accepted which may cause policy translation
  1156  		// in cilium-agent to fail.
  1157  		//
  1158  		// Keep this here so that we can re-introduce this when th minimum suppoerted k8s version
  1159  		// is 1.11.
  1160  		//
  1161  		//AdditionalProperties: &apiextensionsv1beta1.JSONSchemaPropsOrBool{
  1162  		//	Schema: &apiextensionsv1beta1.JSONSchemaProps{
  1163  		//		Type: "string",
  1164  		//	},
  1165  		//},
  1166  	}
  1167  
  1168  	Rule = apiextensionsv1beta1.JSONSchemaProps{
  1169  		Type: "object",
  1170  		Description: "Rule is a policy rule which must be applied to all endpoints which match " +
  1171  			"the labels contained in the endpointSelector\n\nEach rule is split into an " +
  1172  			"ingress section which contains all rules applicable at ingress, and an egress " +
  1173  			"section applicable at egress. For rule types such as `L4Rule` and `CIDR` which " +
  1174  			"can be applied at both ingress and egress, both ingress and egress side have to " +
  1175  			"either specifically allow the connection or one side has to be omitted.\n\n" +
  1176  			"Either ingress, egress, or both can be provided. If both ingress and egress are " +
  1177  			"omitted, the rule has no effect.",
  1178  		Required: []string{
  1179  			"endpointSelector",
  1180  		},
  1181  		Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
  1182  			"Description": {
  1183  				Description: "Description is a free form string, it can be used by the creator " +
  1184  					"of the rule to store human readable explanation of the purpose of this " +
  1185  					"rule. Rules cannot be identified by comment.",
  1186  				Type: "string",
  1187  			},
  1188  			"egress": {
  1189  				Description: "Egress is a list of EgressRule which are enforced at egress. If " +
  1190  					"omitted or empty, this rule does not apply at egress.",
  1191  				Type: "array",
  1192  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
  1193  					Schema: &EgressRule,
  1194  				},
  1195  			},
  1196  			"endpointSelector": initRuleEndpointSelector(),
  1197  			"ingress": {
  1198  				Description: "Ingress is a list of IngressRule which are enforced at ingress. " +
  1199  					"If omitted or empty, this rule does not apply at ingress.",
  1200  				Type: "array",
  1201  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
  1202  					Schema: &IngressRule,
  1203  				},
  1204  			},
  1205  			"labels": {
  1206  				Description: "Labels is a list of optional strings which can be used to " +
  1207  					"re-identify the rule or to store metadata. It is possible to lookup or " +
  1208  					"delete strings based on labels. Labels are not required to be unique, " +
  1209  					"multiple rules can have overlapping or identical labels.",
  1210  				Type: "array",
  1211  				Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
  1212  					Schema: &Label,
  1213  				},
  1214  			},
  1215  		},
  1216  	}
  1217  
  1218  	Service = apiextensionsv1beta1.JSONSchemaProps{
  1219  		Type:        "object",
  1220  		Description: "Service wraps around selectors for services",
  1221  		Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
  1222  			"k8sService":         K8sServiceNamespace,
  1223  			"k8sServiceSelector": initK8sServiceSelector(),
  1224  		},
  1225  	}
  1226  
  1227  	ServiceSelector = apiextensionsv1beta1.JSONSchemaProps{
  1228  		Type:        "object",
  1229  		Description: "ServiceSelector is a label selector for k8s services",
  1230  		Required: []string{
  1231  			"selector",
  1232  		},
  1233  		Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
  1234  			"selector": EndpointSelector,
  1235  			"namespace": {
  1236  				Type: "string",
  1237  			},
  1238  		},
  1239  	}
  1240  
  1241  	spec = initSpec()
  1242  
  1243  	specs = apiextensionsv1beta1.JSONSchemaProps{
  1244  		Description: "Specs is a list of desired Cilium specific rule specification.",
  1245  		Type:        "array",
  1246  		Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{
  1247  			Schema: &spec,
  1248  		},
  1249  	}
  1250  )
  1251  
  1252  func initEndpointSelector() apiextensionsv1beta1.JSONSchemaProps {
  1253  	es := LabelSelector.DeepCopy()
  1254  	es.Description = "EndpointSelector is a wrapper for k8s LabelSelector."
  1255  	return *es
  1256  }
  1257  
  1258  func initPortRule() apiextensionsv1beta1.JSONSchemaProps {
  1259  	portRuleProps := L7Rules.DeepCopy()
  1260  	portRuleProps.Description = "Rules is a list of additional port level rules which must be " +
  1261  		"met in order for the PortRule to allow the traffic. If omitted or empty, " +
  1262  		"no layer 7 rules are enforced."
  1263  	return *portRuleProps
  1264  }
  1265  
  1266  func initRuleEndpointSelector() apiextensionsv1beta1.JSONSchemaProps {
  1267  	ruleProps := EndpointSelector.DeepCopy()
  1268  	ruleProps.Description = "EndpointSelector selects all endpoints which should be subject " +
  1269  		"to this rule. Cannot be empty if nodeSelector is empty."
  1270  	return *ruleProps
  1271  }
  1272  
  1273  func initK8sServiceSelector() apiextensionsv1beta1.JSONSchemaProps {
  1274  	serviceProps := ServiceSelector.DeepCopy()
  1275  	serviceProps.Description = "K8sServiceSelector selects services by k8s labels. " +
  1276  		"Not supported yet"
  1277  	return *serviceProps
  1278  }
  1279  
  1280  func initSpec() apiextensionsv1beta1.JSONSchemaProps {
  1281  	spec := Rule.DeepCopy()
  1282  	spec.Description = "Spec is the desired Cilium specific rule specification."
  1283  	spec.Type = "object"
  1284  	return *spec
  1285  }