github.com/nais/knorten@v0.0.0-20240104110906-55926958e361/pkg/team/k8s.go (about)

     1  package team
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	v1 "k8s.io/api/core/v1"
     8  	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
     9  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    10  	"k8s.io/apimachinery/pkg/runtime/schema"
    11  )
    12  
    13  const (
    14  	k8sLabelEnableTeamNetworkPolicies = "team-netpols"
    15  	replicatorLabel                   = "app.kubernetes.io/managed-by=replicator"
    16  )
    17  
    18  func (c Client) k8sNamespaceExists(ctx context.Context, namespace string) (bool, error) {
    19  	if c.dryRun {
    20  		return false, nil
    21  	}
    22  
    23  	_, err := c.k8sClient.CoreV1().Namespaces().Get(ctx, namespace, metav1.GetOptions{})
    24  	if err != nil {
    25  		if k8sErrors.IsNotFound(err) {
    26  			return false, nil
    27  		}
    28  
    29  		return false, err
    30  	}
    31  
    32  	return true, nil
    33  }
    34  
    35  func (c Client) createK8sNamespace(ctx context.Context, name string) error {
    36  	if c.dryRun {
    37  		return nil
    38  	}
    39  
    40  	namespace := &v1.Namespace{
    41  		ObjectMeta: metav1.ObjectMeta{
    42  			Name: name,
    43  			Labels: map[string]string{
    44  				"team-namespace": "true",
    45  			},
    46  		},
    47  	}
    48  
    49  	_, err := c.k8sClient.CoreV1().Namespaces().Create(ctx, namespace, metav1.CreateOptions{})
    50  	if err != nil && !k8sErrors.IsAlreadyExists(err) {
    51  		return err
    52  	}
    53  
    54  	return nil
    55  }
    56  
    57  func (c Client) deleteK8sNamespace(ctx context.Context, namespace string) error {
    58  	if c.dryRun {
    59  		return nil
    60  	}
    61  
    62  	err := c.k8sClient.CoreV1().Namespaces().Delete(ctx, namespace, metav1.DeleteOptions{})
    63  	if err != nil && !k8sErrors.IsNotFound(err) {
    64  		return err
    65  	}
    66  
    67  	return nil
    68  }
    69  
    70  func (c Client) k8sServiceAccountExists(ctx context.Context, teamID, namespace string) (bool, error) {
    71  	if c.dryRun {
    72  		return false, nil
    73  	}
    74  
    75  	_, err := c.k8sClient.CoreV1().ServiceAccounts(namespace).Get(ctx, teamID, metav1.GetOptions{})
    76  	if err != nil {
    77  		if k8sErrors.IsNotFound(err) {
    78  			return false, nil
    79  		}
    80  
    81  		return false, err
    82  	}
    83  
    84  	return true, nil
    85  }
    86  
    87  func (c Client) createK8sServiceAccount(ctx context.Context, teamID, namespace string) error {
    88  	if c.dryRun {
    89  		return nil
    90  	}
    91  
    92  	saSpec := &v1.ServiceAccount{
    93  		ObjectMeta: metav1.ObjectMeta{
    94  			Name:      teamID,
    95  			Namespace: namespace,
    96  			Annotations: map[string]string{
    97  				"iam.gke.io/gcp-service-account": fmt.Sprintf("%v@%v.iam.gserviceaccount.com", teamID, c.gcpProject),
    98  			},
    99  		},
   100  	}
   101  
   102  	_, err := c.k8sClient.CoreV1().ServiceAccounts(namespace).Create(ctx, saSpec, metav1.CreateOptions{})
   103  	if err != nil && !k8sErrors.IsAlreadyExists(err) {
   104  		return err
   105  	}
   106  
   107  	return nil
   108  }
   109  
   110  func (c Client) defaultEgressNetpolsSync(ctx context.Context, namespace string, restrictEgress bool) error {
   111  	if c.dryRun {
   112  		return nil
   113  	}
   114  
   115  	nsSpec, err := c.k8sClient.CoreV1().Namespaces().Get(ctx, namespace, metav1.GetOptions{})
   116  	if err != nil {
   117  		return err
   118  	}
   119  
   120  	if restrictEgress {
   121  		nsSpec.Labels[k8sLabelEnableTeamNetworkPolicies] = "true"
   122  	} else {
   123  		delete(nsSpec.Labels, k8sLabelEnableTeamNetworkPolicies)
   124  		err := c.k8sClient.NetworkingV1().NetworkPolicies(namespace).Delete(ctx, k8sLabelEnableTeamNetworkPolicies, metav1.DeleteOptions{})
   125  		if err != nil && !k8sErrors.IsNotFound(err) {
   126  			return err
   127  		}
   128  
   129  		if err := c.removeReplicatorNetpols(ctx, namespace); err != nil {
   130  			return err
   131  		}
   132  	}
   133  
   134  	_, err = c.k8sClient.CoreV1().Namespaces().Update(ctx, nsSpec, metav1.UpdateOptions{})
   135  	if err != nil {
   136  		return err
   137  	}
   138  
   139  	return nil
   140  }
   141  
   142  func (c Client) removeReplicatorNetpols(ctx context.Context, namespace string) error {
   143  	if err := c.removeFQDNNetpols(ctx, namespace); err != nil {
   144  		return err
   145  	}
   146  
   147  	return c.removeRegularNetpols(ctx, namespace)
   148  }
   149  
   150  func (c Client) removeFQDNNetpols(ctx context.Context, namespace string) error {
   151  	fqdnNetpolResource := schema.GroupVersionResource{
   152  		Group:    "networking.gke.io",
   153  		Version:  "v1alpha3",
   154  		Resource: "fqdnnetworkpolicies",
   155  	}
   156  	fqdnNetpols, err := c.k8sDynamicClient.Resource(fqdnNetpolResource).Namespace(namespace).List(ctx, metav1.ListOptions{
   157  		LabelSelector: replicatorLabel,
   158  	})
   159  	if err != nil {
   160  		return err
   161  	}
   162  
   163  	for _, fqdn := range fqdnNetpols.Items {
   164  		if err := c.k8sDynamicClient.Resource(fqdnNetpolResource).Namespace(namespace).Delete(ctx, fqdn.GetName(), metav1.DeleteOptions{}); err != nil {
   165  			return err
   166  		}
   167  	}
   168  
   169  	return nil
   170  }
   171  
   172  func (c Client) removeRegularNetpols(ctx context.Context, namespace string) error {
   173  	netpols, err := c.k8sClient.NetworkingV1().NetworkPolicies(namespace).List(ctx, metav1.ListOptions{
   174  		LabelSelector: replicatorLabel,
   175  	})
   176  	if err != nil {
   177  		return err
   178  	}
   179  
   180  	for _, netpol := range netpols.Items {
   181  		err := c.k8sClient.NetworkingV1().NetworkPolicies(namespace).Delete(ctx, netpol.Name, metav1.DeleteOptions{})
   182  		if err != nil && !k8sErrors.IsNotFound(err) {
   183  			return err
   184  		}
   185  	}
   186  
   187  	return nil
   188  }