volcano.sh/volcano@v1.9.0/cmd/webhook-manager/app/util.go (about)

     1  /*
     2  Copyright 2019 The Volcano Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package app
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"crypto/tls"
    23  	"crypto/x509"
    24  	"fmt"
    25  	"strings"
    26  	"time"
    27  
    28  	v1 "k8s.io/api/admissionregistration/v1"
    29  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/apimachinery/pkg/util/wait"
    32  	"k8s.io/client-go/kubernetes"
    33  	"k8s.io/client-go/rest"
    34  	"k8s.io/klog/v2"
    35  
    36  	"volcano.sh/apis/pkg/client/clientset/versioned"
    37  	"volcano.sh/volcano/cmd/webhook-manager/app/options"
    38  	"volcano.sh/volcano/pkg/webhooks/router"
    39  )
    40  
    41  const volcanoAdmissionPrefix = "volcano-admission-service"
    42  
    43  func addCaCertForWebhook(kubeClient *kubernetes.Clientset, service *router.AdmissionService, caBundle []byte) error {
    44  	if service.MutatingConfig != nil {
    45  		// update MutatingWebhookConfigurations
    46  		var mutatingWebhookName = volcanoAdmissionPrefix + strings.ReplaceAll(service.Path, "/", "-")
    47  		var mutatingWebhook *v1.MutatingWebhookConfiguration
    48  		webhookChanged := false
    49  		if err := wait.Poll(time.Second, 5*time.Minute, func() (done bool, err error) {
    50  			mutatingWebhook, err = kubeClient.AdmissionregistrationV1().MutatingWebhookConfigurations().Get(context.TODO(), mutatingWebhookName, metav1.GetOptions{})
    51  			if err != nil {
    52  				if apierrors.IsNotFound(err) {
    53  					klog.Errorln(err)
    54  					return false, nil
    55  				}
    56  				return false, fmt.Errorf("failed to get mutating webhook %v", err)
    57  			}
    58  			return true, nil
    59  		}); err != nil {
    60  			return fmt.Errorf("failed to get mutating webhook %v", err)
    61  		}
    62  
    63  		for index := 0; index < len(mutatingWebhook.Webhooks); index++ {
    64  			if mutatingWebhook.Webhooks[index].ClientConfig.CABundle == nil ||
    65  				!bytes.Equal(mutatingWebhook.Webhooks[index].ClientConfig.CABundle, caBundle) {
    66  				mutatingWebhook.Webhooks[index].ClientConfig.CABundle = caBundle
    67  				webhookChanged = true
    68  			}
    69  		}
    70  		if webhookChanged {
    71  			if _, err := kubeClient.AdmissionregistrationV1().MutatingWebhookConfigurations().Update(context.TODO(), mutatingWebhook, metav1.UpdateOptions{}); err != nil {
    72  				return fmt.Errorf("failed to update mutating admission webhooks %v %v", mutatingWebhookName, err)
    73  			}
    74  		}
    75  	}
    76  
    77  	if service.ValidatingConfig != nil {
    78  		// update ValidatingWebhookConfigurations
    79  		var validatingWebhookName = volcanoAdmissionPrefix + strings.ReplaceAll(service.Path, "/", "-")
    80  		var validatingWebhook *v1.ValidatingWebhookConfiguration
    81  		webhookChanged := false
    82  		if err := wait.Poll(time.Second, 5*time.Minute, func() (done bool, err error) {
    83  			validatingWebhook, err = kubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations().Get(context.TODO(), validatingWebhookName, metav1.GetOptions{})
    84  			if err != nil {
    85  				if apierrors.IsNotFound(err) {
    86  					klog.Errorln(err)
    87  					return false, nil
    88  				}
    89  				return false, fmt.Errorf("failed to get validating webhook %v", err)
    90  			}
    91  			return true, nil
    92  		}); err != nil {
    93  			return fmt.Errorf("failed to get validating webhook %v", err)
    94  		}
    95  
    96  		for index := 0; index < len(validatingWebhook.Webhooks); index++ {
    97  			if validatingWebhook.Webhooks[index].ClientConfig.CABundle == nil ||
    98  				!bytes.Equal(validatingWebhook.Webhooks[index].ClientConfig.CABundle, caBundle) {
    99  				validatingWebhook.Webhooks[index].ClientConfig.CABundle = caBundle
   100  				webhookChanged = true
   101  			}
   102  		}
   103  		if webhookChanged {
   104  			if _, err := kubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations().Update(context.TODO(), validatingWebhook, metav1.UpdateOptions{}); err != nil {
   105  				return fmt.Errorf("failed to update validating admission webhooks %v %v", validatingWebhookName, err)
   106  			}
   107  		}
   108  	}
   109  
   110  	return nil
   111  }
   112  
   113  // getKubeClient Get a clientset with restConfig.
   114  func getKubeClient(restConfig *rest.Config) *kubernetes.Clientset {
   115  	clientset, err := kubernetes.NewForConfig(restConfig)
   116  	if err != nil {
   117  		klog.Fatal(err)
   118  	}
   119  	return clientset
   120  }
   121  
   122  // GetVolcanoClient get a clientset for volcano.
   123  func getVolcanoClient(restConfig *rest.Config) *versioned.Clientset {
   124  	clientset, err := versioned.NewForConfig(restConfig)
   125  	if err != nil {
   126  		klog.Fatal(err)
   127  	}
   128  	return clientset
   129  }
   130  
   131  // configTLS is a helper function that generate tls certificates from directly defined tls config or kubeconfig
   132  // These are passed in as command line for cluster certification. If tls config is passed in, we use the directly
   133  // defined tls config, else use that defined in kubeconfig.
   134  func configTLS(config *options.Config, restConfig *rest.Config) *tls.Config {
   135  	if len(config.CertData) != 0 && len(config.KeyData) != 0 {
   136  		certPool := x509.NewCertPool()
   137  		certPool.AppendCertsFromPEM(config.CaCertData)
   138  
   139  		sCert, err := tls.X509KeyPair(config.CertData, config.KeyData)
   140  		if err != nil {
   141  			klog.Fatal(err)
   142  		}
   143  
   144  		return &tls.Config{
   145  			Certificates: []tls.Certificate{sCert},
   146  			RootCAs:      certPool,
   147  			MinVersion:   tls.VersionTLS12,
   148  			ClientAuth:   tls.VerifyClientCertIfGiven,
   149  			CipherSuites: []uint16{
   150  				tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
   151  				tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
   152  				tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
   153  			},
   154  		}
   155  	}
   156  
   157  	if len(restConfig.CertData) != 0 && len(restConfig.KeyData) != 0 {
   158  		sCert, err := tls.X509KeyPair(restConfig.CertData, restConfig.KeyData)
   159  		if err != nil {
   160  			klog.Fatal(err)
   161  		}
   162  
   163  		return &tls.Config{
   164  			Certificates: []tls.Certificate{sCert},
   165  		}
   166  	}
   167  
   168  	klog.Fatal("tls: failed to find any tls config data")
   169  	return &tls.Config{}
   170  }