...

Source file src/github.com/redhat-developer/odo/pkg/kclient/namespace.go

Documentation: github.com/redhat-developer/odo/pkg/kclient

     1  package kclient
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"time"
     8  
     9  	corev1 "k8s.io/api/core/v1"
    10  	kerrors "k8s.io/apimachinery/pkg/api/errors"
    11  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  	"k8s.io/apimachinery/pkg/fields"
    13  	"k8s.io/apimachinery/pkg/watch"
    14  	"k8s.io/client-go/tools/clientcmd"
    15  	"k8s.io/klog"
    16  	"k8s.io/pod-security-admission/api"
    17  	psaApi "k8s.io/pod-security-admission/api"
    18  )
    19  
    20  const (
    21  	// timeout for waiting for namespace deletion
    22  	waitForNamespaceDeletionTimeOut = 3 * time.Minute
    23  
    24  	// timeout for getting the default service account
    25  	getDefaultServiceAccTimeout = 1 * time.Minute
    26  )
    27  
    28  // GetNamespaces return list of existing namespaces that user has access to.
    29  func (c *Client) GetNamespaces() ([]string, error) {
    30  	namespaces, err := c.KubeClient.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
    31  	if err != nil {
    32  		return nil, fmt.Errorf("unable to list namespaces: %w", err)
    33  	}
    34  
    35  	var names []string
    36  	for _, p := range namespaces.Items {
    37  		names = append(names, p.Name)
    38  	}
    39  	return names, nil
    40  }
    41  
    42  // GetNamespace returns Namespace based on its name
    43  // Errors related to project not being found or forbidden are translated to nil project for compatibility
    44  func (c *Client) GetNamespace(name string) (*corev1.Namespace, error) {
    45  	ns, err := c.KubeClient.CoreV1().Namespaces().Get(context.TODO(), name, metav1.GetOptions{})
    46  	if err != nil {
    47  		istatus, ok := err.(kerrors.APIStatus)
    48  		if ok {
    49  			status := istatus.Status()
    50  			if status.Reason == metav1.StatusReasonNotFound || status.Reason == metav1.StatusReasonForbidden {
    51  				return nil, nil
    52  			}
    53  		} else {
    54  			return nil, err
    55  		}
    56  
    57  	}
    58  	return ns, err
    59  
    60  }
    61  
    62  // GetNamespace returns Namespace based on its name
    63  func (c *Client) GetNamespaceNormal(name string) (*corev1.Namespace, error) {
    64  	return c.KubeClient.CoreV1().Namespaces().Get(context.TODO(), name, metav1.GetOptions{})
    65  }
    66  
    67  // CreateNamespace creates new namespace
    68  func (c *Client) CreateNamespace(name string) (*corev1.Namespace, error) {
    69  	namespace := &corev1.Namespace{
    70  		ObjectMeta: metav1.ObjectMeta{
    71  			Name: name,
    72  		},
    73  	}
    74  
    75  	newNamespace, err := c.KubeClient.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{FieldManager: FieldManager})
    76  	if err != nil {
    77  		return nil, fmt.Errorf("unable to create Namespace %s: %w", namespace.ObjectMeta.Name, err)
    78  	}
    79  	return newNamespace, nil
    80  }
    81  
    82  // DeleteNamespace deletes namespace
    83  // if wait=true , it will wait for deletion
    84  func (c *Client) DeleteNamespace(name string, wait bool) error {
    85  	var watcher watch.Interface
    86  	var err error
    87  	if wait {
    88  		watcher, err = c.KubeClient.CoreV1().Namespaces().Watch(context.TODO(), metav1.ListOptions{
    89  			FieldSelector: fields.Set{"metadata.name": name}.AsSelector().String(),
    90  		})
    91  		if err != nil {
    92  			return fmt.Errorf("unable to watch namespace: %w", err)
    93  		}
    94  		defer watcher.Stop()
    95  	}
    96  
    97  	err = c.KubeClient.CoreV1().Namespaces().Delete(context.TODO(), name, metav1.DeleteOptions{})
    98  	if err != nil {
    99  		return fmt.Errorf("unable to delete Namespace %s: %w", name, err)
   100  	}
   101  
   102  	if watcher != nil {
   103  		namespaceChannel := make(chan *corev1.Namespace)
   104  		watchErrorChannel := make(chan error)
   105  
   106  		go func() {
   107  			for {
   108  				val, ok := <-watcher.ResultChan()
   109  				if !ok {
   110  					watchErrorChannel <- fmt.Errorf("watch channel was closed unexpectedly: %+v", val)
   111  					break
   112  				}
   113  				klog.V(3).Infof("Watch event.Type '%s'.", val.Type)
   114  
   115  				if namespaceStatus, ok := val.Object.(*corev1.Namespace); ok {
   116  					klog.V(3).Infof("Status of delete of namespace %s is '%s'.", name, namespaceStatus.Status.Phase)
   117  					if val.Type == watch.Deleted {
   118  						namespaceChannel <- namespaceStatus
   119  						break
   120  					}
   121  					if val.Type == watch.Error {
   122  						watchErrorChannel <- fmt.Errorf("failed watching the deletion of namespace %s", name)
   123  						break
   124  					}
   125  
   126  				}
   127  
   128  			}
   129  			close(namespaceChannel)
   130  			close(watchErrorChannel)
   131  		}()
   132  
   133  		select {
   134  		case val := <-namespaceChannel:
   135  			klog.V(3).Infof("Namespace %s deleted", val.Name)
   136  			return nil
   137  		case err := <-watchErrorChannel:
   138  			return err
   139  		case <-time.After(waitForNamespaceDeletionTimeOut):
   140  			return fmt.Errorf("waited %s but couldn't delete namespace %s in time", waitForNamespaceDeletionTimeOut, name)
   141  		}
   142  
   143  	}
   144  	return nil
   145  }
   146  
   147  // SetCurrentNamespace change current namespace in kubeconfig
   148  func (c *Client) SetCurrentNamespace(namespace string) error {
   149  	rawConfig, err := c.KubeConfig.RawConfig()
   150  	if err != nil {
   151  		return fmt.Errorf("unable to switch to %s project: %w", namespace, err)
   152  	}
   153  
   154  	rawConfig.Contexts[rawConfig.CurrentContext].Namespace = namespace
   155  
   156  	err = clientcmd.ModifyConfig(clientcmd.NewDefaultClientConfigLoadingRules(), rawConfig, true)
   157  	if err != nil {
   158  		return fmt.Errorf("unable to switch to %s project: %w", namespace, err)
   159  	}
   160  
   161  	c.Namespace = namespace
   162  	return nil
   163  }
   164  
   165  func (c *Client) GetCurrentNamespace() string {
   166  	return c.Namespace
   167  }
   168  
   169  func (c *Client) SetNamespace(ns string) {
   170  	c.Namespace = ns
   171  }
   172  
   173  // WaitForServiceAccountInNamespace waits for the given service account to be ready
   174  func (c *Client) WaitForServiceAccountInNamespace(namespace, serviceAccountName string) error {
   175  	if namespace == "" || serviceAccountName == "" {
   176  		return errors.New("namespace and serviceAccountName cannot be empty")
   177  	}
   178  	watcher, err := c.KubeClient.CoreV1().ServiceAccounts(namespace).Watch(context.TODO(), metav1.SingleObject(metav1.ObjectMeta{Name: serviceAccountName}))
   179  	if err != nil {
   180  		return err
   181  	}
   182  
   183  	timeout := time.After(getDefaultServiceAccTimeout)
   184  	if watcher != nil {
   185  		defer watcher.Stop()
   186  		for {
   187  			select {
   188  			case val, ok := <-watcher.ResultChan():
   189  				if !ok {
   190  					break
   191  				}
   192  				if serviceAccount, ok := val.Object.(*corev1.ServiceAccount); ok {
   193  					if serviceAccount.Name == serviceAccountName {
   194  						klog.V(3).Infof("Status of creation of service account %s is ready", serviceAccount)
   195  						return nil
   196  					}
   197  				}
   198  			case <-timeout:
   199  				return errors.New("timed out waiting for service to be ready")
   200  			}
   201  		}
   202  	}
   203  	return nil
   204  }
   205  
   206  func (c *Client) GetCurrentNamespacePolicy() (psaApi.Policy, error) {
   207  	ns, err := c.GetNamespaceNormal(c.GetCurrentNamespace())
   208  	if err != nil {
   209  		return psaApi.Policy{}, err
   210  	}
   211  	labels := ns.GetLabels()
   212  	policy, _ := api.PolicyToEvaluate(labels, api.Policy{})
   213  	return policy, nil
   214  }
   215  

View as plain text