github.com/fafucoder/cilium@v1.6.11/cilium/cmd/preflight_k8s_valid_cnp.go (about)

     1  // Copyright 2020 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 cmd
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  
    21  	"github.com/cilium/cilium/pkg/k8s"
    22  	v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
    23  	"github.com/cilium/cilium/pkg/k8s/client/clientset/versioned/scheme"
    24  	"github.com/cilium/cilium/pkg/logging"
    25  	"github.com/cilium/cilium/pkg/option"
    26  
    27  	"github.com/spf13/cobra"
    28  	"github.com/spf13/viper"
    29  	apiextensionsinternal "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
    30  	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
    31  	"k8s.io/apiextensions-apiserver/pkg/apiserver/validation"
    32  	apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
    33  	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
    34  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    35  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    36  )
    37  
    38  var validateCNP = &cobra.Command{
    39  	Use:   "validate-cnp",
    40  	Short: "Validate Cilium Network Policies deployed in the cluster",
    41  	Long: `Before upgrading Cilium it is recommended to run this validation checker
    42  to make sure the policies deployed are valid. The validator will verify if all policies
    43  deployed in the cluster are valid, in case they are not, an error is printed and the
    44  has an exit code -1 is returned.`,
    45  	Run: func(cmd *cobra.Command, args []string) {
    46  		err := validateCNPs()
    47  		if err != nil {
    48  			log.Error(err)
    49  			os.Exit(-1)
    50  		}
    51  	},
    52  }
    53  
    54  const (
    55  	ciliumGroup = "cilium.io"
    56  )
    57  
    58  func validateCNPs() error {
    59  	// The internal packages log things. Make sure they follow the setup of of
    60  	// the CLI tool.
    61  	logging.DefaultLogger.SetFormatter(log.Formatter)
    62  
    63  	log.Info("Setting up Kubernetes client")
    64  
    65  	k8sClientQPSLimit := viper.GetFloat64(option.K8sClientQPSLimit)
    66  	k8sClientBurst := viper.GetInt(option.K8sClientBurst)
    67  
    68  	k8s.Configure(k8sAPIServer, k8sKubeConfigPath, float32(k8sClientQPSLimit), k8sClientBurst)
    69  
    70  	if err := k8s.Init(); err != nil {
    71  		log.WithError(err).Fatal("Unable to connect to Kubernetes apiserver")
    72  	}
    73  
    74  	restConfig, err := k8s.CreateConfig()
    75  	if err != nil {
    76  		return fmt.Errorf("Unable to create rest configuration for k8s CRD: %s", err)
    77  	}
    78  	apiExtensionsClient, err := apiextensionsclient.NewForConfig(restConfig)
    79  	if err != nil {
    80  		return fmt.Errorf("Unable to create API extensions clientset for k8s CRD: %s", err)
    81  	}
    82  
    83  	cnpErr := validateNPResources(apiExtensionsClient, &v2.CNPCRV, "ciliumnetworkpolicies", "CiliumNetworkPolicy")
    84  
    85  	if cnpErr != nil {
    86  		return cnpErr
    87  	}
    88  	log.Info("All CNPs valid!")
    89  	return nil
    90  }
    91  
    92  func validateNPResources(apiExtensionsClient apiextensionsclient.Interface, crv *v1beta1.CustomResourceValidation, name, shortName string) error {
    93  	// check if the crd is installed at all
    94  	_, err := apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(name+"."+ciliumGroup, metav1.GetOptions{})
    95  	switch {
    96  	case err == nil:
    97  	case k8sErrors.IsNotFound(err):
    98  		return nil
    99  	default:
   100  		return err
   101  	}
   102  
   103  	var internal apiextensionsinternal.CustomResourceValidation
   104  	err = v1beta1.Convert_v1beta1_CustomResourceValidation_To_apiextensions_CustomResourceValidation(
   105  		crv,
   106  		&internal,
   107  		nil,
   108  	)
   109  	if err != nil {
   110  		return err
   111  	}
   112  	validator, _, err := validation.NewSchemaValidator(&internal)
   113  	if err != nil {
   114  		return err
   115  	}
   116  
   117  	var (
   118  		policyErr error
   119  		cnps      unstructured.UnstructuredList
   120  	)
   121  	for {
   122  		opts := metav1.ListOptions{
   123  			Limit:    25,
   124  			Continue: cnps.GetContinue(),
   125  		}
   126  		err = k8s.CiliumClient().
   127  			Interface.
   128  			CiliumV2().
   129  			RESTClient().
   130  			Get().
   131  			VersionedParams(&opts, scheme.ParameterCodec).
   132  			Resource(name).
   133  			Do().
   134  			Into(&cnps)
   135  		if err != nil {
   136  			return err
   137  		}
   138  
   139  		for _, cnp := range cnps.Items {
   140  			cnpName := fmt.Sprintf("%s/%s", cnp.GetNamespace(), cnp.GetName())
   141  			if errs := validation.ValidateCustomResource(nil, &cnp, validator); len(errs) > 0 {
   142  				log.Errorf("Validating %s '%s': unexpected validation error: %s",
   143  					shortName, cnpName, errs.ToAggregate())
   144  				policyErr = fmt.Errorf("Found invalid %s", shortName)
   145  			} else {
   146  				log.Infof("Validating %s '%s': OK!", shortName, cnpName)
   147  			}
   148  		}
   149  		if cnps.GetContinue() == "" {
   150  			break
   151  		}
   152  	}
   153  	return policyErr
   154  }