github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/cloud/pkg/admissioncontroller/admission.go (about)

     1  package admissioncontroller
     2  
     3  import (
     4  	"crypto/tls"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  
     9  	admissionv1beta1 "k8s.io/api/admission/v1beta1"
    10  	admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
    11  	corev1 "k8s.io/api/core/v1"
    12  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    13  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  	"k8s.io/apimachinery/pkg/runtime"
    15  	"k8s.io/apimachinery/pkg/runtime/serializer"
    16  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    17  	"k8s.io/client-go/kubernetes"
    18  	admissionregistrationv1beta1client "k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1"
    19  	restclient "k8s.io/client-go/rest"
    20  	"k8s.io/client-go/tools/clientcmd"
    21  	"k8s.io/klog"
    22  
    23  	"github.com/kubeedge/kubeedge/cloud/cmd/admission/app/options"
    24  	"github.com/kubeedge/kubeedge/cloud/pkg/apis/devices/v1alpha1"
    25  )
    26  
    27  const (
    28  	ValidateDeviceModelConfigName  = "validate-devicemodel"
    29  	ValidateDeviceModelWebhookName = "validatedevicemodel.kubeedge.io"
    30  )
    31  
    32  var scheme = runtime.NewScheme()
    33  
    34  //codecs is for retrieving serializers for the supported wire formats
    35  //and conversion wrappers to define preferred internal and external versions.
    36  var codecs = serializer.NewCodecFactory(scheme)
    37  
    38  func init() {
    39  	addToScheme(scheme)
    40  }
    41  
    42  func addToScheme(scheme *runtime.Scheme) {
    43  	utilruntime.Must(corev1.AddToScheme(scheme))
    44  	utilruntime.Must(admissionv1beta1.AddToScheme(scheme))
    45  	utilruntime.Must(admissionregistrationv1beta1.AddToScheme(scheme))
    46  	utilruntime.Must(addDeviceCrds(scheme))
    47  }
    48  
    49  // TODO: move this func to apis/devices/v1alpha1/register.go
    50  func addDeviceCrds(scheme *runtime.Scheme) error {
    51  	// Add Device
    52  	scheme.AddKnownTypes(v1alpha1.SchemeGroupVersion, &v1alpha1.Device{}, &v1alpha1.DeviceList{})
    53  	metav1.AddToGroupVersion(scheme, v1alpha1.SchemeGroupVersion)
    54  	// Add DeviceModel
    55  	scheme.AddKnownTypes(v1alpha1.SchemeGroupVersion, &v1alpha1.DeviceModel{}, &v1alpha1.DeviceModelList{})
    56  	metav1.AddToGroupVersion(scheme, v1alpha1.SchemeGroupVersion)
    57  
    58  	return nil
    59  }
    60  
    61  // AdmissionController implements the admission webhook for validation of configuration.
    62  type AdmissionController struct {
    63  	Client *kubernetes.Clientset
    64  }
    65  
    66  func strPtr(s string) *string { return &s }
    67  
    68  // Run starts the webhook service
    69  func Run(opt *options.AdmissionOptions) {
    70  	klog.V(4).Infof("AdmissionOptions: %++v", *opt)
    71  	restConfig, err := clientcmd.BuildConfigFromFlags(opt.Master, opt.Kubeconfig)
    72  	if err != nil {
    73  		klog.Fatal(err)
    74  	}
    75  
    76  	cli, err := kubernetes.NewForConfig(restConfig)
    77  	if err != nil {
    78  		klog.Fatalf("Create kube client failed with error: %v", err)
    79  	}
    80  
    81  	ac := AdmissionController{}
    82  	ac.Client = cli
    83  
    84  	caBundle, err := ioutil.ReadFile(opt.CaCertFile)
    85  	if err != nil {
    86  		klog.Fatalf("Unable to read cacert file: %v\n", err)
    87  	}
    88  
    89  	//TODO: read somewhere to get what's kind of webhook is enabled, register those webhook only.
    90  	err = ac.registerWebhooks(opt, caBundle)
    91  	if err != nil {
    92  		klog.Fatalf("Failed to register the webhook with error: %v", err)
    93  	}
    94  
    95  	http.HandleFunc("/devicemodels", serveDeviceModel)
    96  
    97  	server := &http.Server{
    98  		Addr:      fmt.Sprintf(":%v", opt.Port),
    99  		TLSConfig: configTLS(opt, restConfig),
   100  	}
   101  
   102  	server.ListenAndServeTLS("", "")
   103  }
   104  
   105  // configTLS is a helper function that generate tls certificates from directly defined tls config or kubeconfig
   106  // These are passed in as command line for cluster certification. If tls config is passed in, we use the directly
   107  // defined tls config, else use that defined in kubeconfig
   108  func configTLS(opt *options.AdmissionOptions, restConfig *restclient.Config) *tls.Config {
   109  	if len(opt.CertFile) != 0 && len(opt.KeyFile) != 0 {
   110  		sCert, err := tls.LoadX509KeyPair(opt.CertFile, opt.KeyFile)
   111  		if err != nil {
   112  			klog.Fatal(err)
   113  		}
   114  
   115  		return &tls.Config{
   116  			Certificates: []tls.Certificate{sCert},
   117  		}
   118  	}
   119  
   120  	if len(restConfig.CertData) != 0 && len(restConfig.KeyData) != 0 {
   121  		sCert, err := tls.X509KeyPair(restConfig.CertData, restConfig.KeyData)
   122  		if err != nil {
   123  			klog.Fatal(err)
   124  		}
   125  
   126  		return &tls.Config{
   127  			Certificates: []tls.Certificate{sCert},
   128  		}
   129  	}
   130  
   131  	klog.Fatal("tls: failed to find any tls config data")
   132  	return &tls.Config{}
   133  }
   134  
   135  // registerWebhooks registers the admission webhook.
   136  func (ac *AdmissionController) registerWebhooks(opt *options.AdmissionOptions, cabundle []byte) error {
   137  	ignorePolicy := admissionregistrationv1beta1.Ignore
   138  	deviceModelCRDWebhook := admissionregistrationv1beta1.ValidatingWebhookConfiguration{
   139  		ObjectMeta: metav1.ObjectMeta{
   140  			Name: ValidateDeviceModelConfigName,
   141  		},
   142  		Webhooks: []admissionregistrationv1beta1.ValidatingWebhook{
   143  			{
   144  				Name: ValidateDeviceModelWebhookName,
   145  				Rules: []admissionregistrationv1beta1.RuleWithOperations{{
   146  					Operations: []admissionregistrationv1beta1.OperationType{
   147  						admissionregistrationv1beta1.Create,
   148  						admissionregistrationv1beta1.Update,
   149  					},
   150  					Rule: admissionregistrationv1beta1.Rule{
   151  						APIGroups:   []string{"devices.kubeedge.io"},
   152  						APIVersions: []string{"v1alpha1"},
   153  						Resources:   []string{"devicemodels"},
   154  					},
   155  				}},
   156  				ClientConfig: admissionregistrationv1beta1.WebhookClientConfig{
   157  					Service: &admissionregistrationv1beta1.ServiceReference{
   158  						Namespace: opt.AdmissionServiceNamespace,
   159  						Name:      opt.AdmissionServiceName,
   160  						Path:      strPtr("/devicemodels"),
   161  						Port:      &opt.Port,
   162  					},
   163  					CABundle: cabundle,
   164  				},
   165  				FailurePolicy: &ignorePolicy,
   166  			},
   167  		},
   168  	}
   169  
   170  	if err := registerValidateWebhook(ac.Client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations(),
   171  		[]admissionregistrationv1beta1.ValidatingWebhookConfiguration{deviceModelCRDWebhook}); err != nil {
   172  		return err
   173  	}
   174  	return nil
   175  }
   176  
   177  func registerValidateWebhook(client admissionregistrationv1beta1client.ValidatingWebhookConfigurationInterface,
   178  	webhooks []admissionregistrationv1beta1.ValidatingWebhookConfiguration) error {
   179  	for _, hook := range webhooks {
   180  		existing, err := client.Get(hook.Name, metav1.GetOptions{})
   181  		if err != nil && !apierrors.IsNotFound(err) {
   182  			return err
   183  		}
   184  		if err == nil && existing != nil {
   185  			existing.Webhooks = hook.Webhooks
   186  			klog.Infof("Updating ValidatingWebhookConfiguration: %v", hook.Name)
   187  			if _, err := client.Update(existing); err != nil {
   188  				return err
   189  			}
   190  		} else {
   191  			klog.Infof("Creating ValidatingWebhookConfiguration: %v", hook.Name)
   192  			if _, err := client.Create(&hook); err != nil {
   193  				return err
   194  			}
   195  		}
   196  	}
   197  	return nil
   198  }