istio.io/istio@v0.0.0-20240520182934-d79c90f27776/security/pkg/k8s/configutil.go (about)

     1  // Copyright Istio Authors
     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 k8s
    16  
    17  import (
    18  	"fmt"
    19  
    20  	v1 "k8s.io/api/core/v1"
    21  	"k8s.io/apimachinery/pkg/api/errors"
    22  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    23  
    24  	"istio.io/istio/pkg/config/constants"
    25  	"istio.io/istio/pkg/kube/kclient"
    26  	"istio.io/istio/pkg/log"
    27  )
    28  
    29  // InsertDataToConfigMap inserts a data to a configmap in a namespace.
    30  // client: the k8s client interface.
    31  // lister: the configmap lister.
    32  // meta: the metadata of configmap.
    33  // caBundle: ca cert data bytes.
    34  func InsertDataToConfigMap(client kclient.Client[*v1.ConfigMap], meta metav1.ObjectMeta, caBundle []byte) error {
    35  	configmap := client.Get(meta.Name, meta.Namespace)
    36  	if configmap == nil {
    37  		// Create a new ConfigMap.
    38  		configmap = &v1.ConfigMap{
    39  			ObjectMeta: meta,
    40  			Data: map[string]string{
    41  				constants.CACertNamespaceConfigMapDataName: string(caBundle),
    42  			},
    43  		}
    44  		if _, err := client.Create(configmap); err != nil {
    45  			// Namespace may be deleted between now... and our previous check. Just skip this, we cannot create into deleted ns
    46  			// And don't retry a create if the namespace is terminating
    47  			if errors.IsAlreadyExists(err) || errors.HasStatusCause(err, v1.NamespaceTerminatingCause) {
    48  				return nil
    49  			}
    50  			if errors.IsForbidden(err) {
    51  				log.Infof("skip writing ConfigMap %v/%v as we do not have permissions to do so", meta.Namespace, meta.Name)
    52  				return nil
    53  			}
    54  			return fmt.Errorf("error when creating configmap %v: %v", meta.Name, err)
    55  		}
    56  	} else {
    57  		// Otherwise, update the config map if changes are required
    58  		err := updateDataInConfigMap(client, configmap, caBundle)
    59  		if err != nil {
    60  			return err
    61  		}
    62  	}
    63  	return nil
    64  }
    65  
    66  // insertData merges a configmap with a map, and returns true if any changes were made
    67  func insertData(cm *v1.ConfigMap, data map[string]string) bool {
    68  	if cm.Data == nil {
    69  		cm.Data = data
    70  		return true
    71  	}
    72  	needsUpdate := false
    73  	for k, v := range data {
    74  		if cm.Data[k] != v {
    75  			needsUpdate = true
    76  		}
    77  		cm.Data[k] = v
    78  	}
    79  	return needsUpdate
    80  }
    81  
    82  func updateDataInConfigMap(c kclient.Client[*v1.ConfigMap], cm *v1.ConfigMap, caBundle []byte) error {
    83  	if cm == nil {
    84  		return fmt.Errorf("cannot update nil configmap")
    85  	}
    86  	newCm := cm.DeepCopy()
    87  	data := map[string]string{
    88  		constants.CACertNamespaceConfigMapDataName: string(caBundle),
    89  	}
    90  	if needsUpdate := insertData(newCm, data); !needsUpdate {
    91  		return nil
    92  	}
    93  	if _, err := c.Update(newCm); err != nil {
    94  		return fmt.Errorf("error when updating configmap %v: %v", cm.Name, err)
    95  	}
    96  	return nil
    97  }