github.com/metaprov/modela-operator@v0.0.0-20240118193048-f378be8b74d2/pkg/kube/services.go (about)

     1  package kube
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	appsv1 "k8s.io/api/apps/v1"
     7  	rbacv1 "k8s.io/api/rbac/v1"
     8  	"k8s.io/apimachinery/pkg/api/meta"
     9  	"k8s.io/apimachinery/pkg/runtime"
    10  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    11  	"k8s.io/client-go/discovery"
    12  	"k8s.io/client-go/discovery/cached/disk"
    13  	"k8s.io/client-go/restmapper"
    14  	"k8s.io/client-go/util/homedir"
    15  	"path/filepath"
    16  	ctrl "sigs.k8s.io/controller-runtime"
    17  	"sigs.k8s.io/controller-runtime/pkg/client"
    18  	"sigs.k8s.io/controller-runtime/pkg/client/config"
    19  	"strings"
    20  	"time"
    21  
    22  	catalog "github.com/metaprov/modelaapi/pkg/apis/catalog/v1alpha1"
    23  	infra "github.com/metaprov/modelaapi/pkg/apis/infra/v1alpha1"
    24  
    25  	"github.com/pkg/errors"
    26  	corev1 "k8s.io/api/core/v1"
    27  	v1 "k8s.io/api/core/v1"
    28  	nwv1 "k8s.io/api/networking/v1"
    29  	apiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
    30  	k8serr "k8s.io/apimachinery/pkg/api/errors"
    31  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    32  	"k8s.io/client-go/kubernetes"
    33  	"k8s.io/client-go/rest"
    34  	"k8s.io/client-go/tools/clientcmd"
    35  )
    36  
    37  var (
    38  	ClientScheme = runtime.NewScheme()
    39  )
    40  
    41  func init() {
    42  	utilruntime.Must(infra.AddKnownTypes(ClientScheme))
    43  	utilruntime.Must(catalog.AddKnownTypes(ClientScheme))
    44  
    45  	utilruntime.Must(appsv1.AddToScheme(ClientScheme))
    46  	utilruntime.Must(corev1.AddToScheme(ClientScheme))
    47  	utilruntime.Must(rbacv1.AddToScheme(ClientScheme))
    48  	utilruntime.Must(nwv1.AddToScheme(ClientScheme))
    49  
    50  }
    51  
    52  // check if a pod is running, return not nil error if not.
    53  func IsPodRunning(ns string, prefix string) (bool, error) {
    54  	clientSet := kubernetes.NewForConfigOrDie(ctrl.GetConfigOrDie())
    55  	pods, err := clientSet.CoreV1().Pods(ns).List(context.Background(), metav1.ListOptions{})
    56  	if err != nil {
    57  		return false, err
    58  	}
    59  	for _, v := range pods.Items {
    60  		if strings.Contains(v.Name, prefix) {
    61  			return v.Status.Phase == "Running", nil
    62  		}
    63  	}
    64  	return false, nil
    65  }
    66  
    67  func IsDeploymentCreatedByModela(ns string, name string) (bool, error) {
    68  	clientSet := kubernetes.NewForConfigOrDie(ctrl.GetConfigOrDie())
    69  	deployment, err := clientSet.AppsV1().Deployments(ns).Get(context.Background(), name, metav1.GetOptions{})
    70  	if err != nil {
    71  		return false, err
    72  	}
    73  	if val, ok := deployment.GetLabels()["app.kubernetes.io/created-by"]; ok {
    74  		if val == "modela-operator" {
    75  			return true, nil
    76  		}
    77  	}
    78  	return false, nil
    79  }
    80  
    81  func IsStatefulSetCreatedByModela(ns string, name string) (bool, error) {
    82  	clientSet := kubernetes.NewForConfigOrDie(ctrl.GetConfigOrDie())
    83  	statefulSet, err := clientSet.AppsV1().StatefulSets(ns).Get(context.Background(), name, metav1.GetOptions{})
    84  	if err != nil {
    85  		return false, err
    86  	}
    87  	if val, ok := statefulSet.GetLabels()["app.kubernetes.io/created-by"]; ok {
    88  		if val == "modela-operator" {
    89  			return true, nil
    90  		}
    91  	}
    92  	return false, nil
    93  }
    94  
    95  func GetCRDVersion(name string) (string, error) {
    96  	clientSet := apiextensions.NewForConfigOrDie(ctrl.GetConfigOrDie())
    97  	crd, err := clientSet.ApiextensionsV1().CustomResourceDefinitions().Get(context.Background(), name, metav1.GetOptions{})
    98  	if err != nil {
    99  		return "", err
   100  	}
   101  	for _, version := range crd.Spec.Versions {
   102  		if version.Storage {
   103  			return version.Name, nil
   104  		}
   105  	}
   106  	return "", nil
   107  }
   108  
   109  func CreateNamespace(name string, operatorName string) error {
   110  	clientSet := kubernetes.NewForConfigOrDie(ctrl.GetConfigOrDie())
   111  	_, err := clientSet.CoreV1().Namespaces().Get(context.Background(), name, metav1.GetOptions{})
   112  	if k8serr.IsNotFound(err) {
   113  		_, err = clientSet.CoreV1().Namespaces().Create(context.Background(), &v1.Namespace{
   114  			ObjectMeta: metav1.ObjectMeta{
   115  				Name:   name,
   116  				Labels: map[string]string{"management.modela.ai/operator": operatorName},
   117  			},
   118  		}, metav1.CreateOptions{})
   119  		if err != nil {
   120  			return errors.Errorf("Failed to create namespace %s, err: %s", name, err)
   121  		}
   122  	} else {
   123  		return err
   124  	}
   125  	return nil
   126  }
   127  
   128  func IsNamespaceCreated(name string) (bool, error) {
   129  	clientSet := kubernetes.NewForConfigOrDie(ctrl.GetConfigOrDie())
   130  	_, err := clientSet.CoreV1().Namespaces().Get(context.Background(), name, metav1.GetOptions{})
   131  	return !k8serr.IsNotFound(err), nil
   132  }
   133  
   134  func IsNamespaceCreatedByOperator(name string, operator string) (bool, error) {
   135  	clientSet := kubernetes.NewForConfigOrDie(ctrl.GetConfigOrDie())
   136  	namespace, err := clientSet.CoreV1().Namespaces().Get(context.Background(), name, metav1.GetOptions{})
   137  	if err != nil {
   138  		return false, err
   139  	}
   140  	if val, ok := namespace.GetLabels()["management.modela.ai/operator"]; ok {
   141  		if val == operator {
   142  			return true, nil
   143  		}
   144  	}
   145  	return false, nil
   146  }
   147  
   148  func DeleteNamespace(name string) error {
   149  	clientSet := kubernetes.NewForConfigOrDie(ctrl.GetConfigOrDie())
   150  	err := clientSet.CoreV1().Namespaces().Delete(context.Background(), name, metav1.DeleteOptions{})
   151  	if err != nil && !k8serr.IsNotFound(err) {
   152  		return errors.Wrapf(err, "Failed to delete namespace %s", name)
   153  	}
   154  	return nil
   155  }
   156  
   157  func CreateOrUpdateSecret(ns string, name string, values map[string]string) error {
   158  	clientSet := kubernetes.NewForConfigOrDie(ctrl.GetConfigOrDie())
   159  	secret, err := clientSet.CoreV1().Secrets(ns).Get(context.Background(), name, metav1.GetOptions{})
   160  	if k8serr.IsNotFound(err) {
   161  		s := &v1.Secret{
   162  			ObjectMeta: metav1.ObjectMeta{
   163  				Name:      name,
   164  				Namespace: ns,
   165  			},
   166  			StringData: values,
   167  		}
   168  		_, err = clientSet.CoreV1().Secrets(ns).Create(context.Background(), s, metav1.CreateOptions{})
   169  		if err != nil {
   170  			return errors.Errorf("Failed to create namespace %s, err: %s", name, err)
   171  		}
   172  	} else if err != nil {
   173  		return errors.Errorf("Error getting namespace %s, err: %s", name, err)
   174  	} else {
   175  		for k, v := range values {
   176  			secret.Data[k] = []byte(v)
   177  		}
   178  		_, err = clientSet.CoreV1().Secrets(ns).Update(context.Background(), secret, metav1.UpdateOptions{})
   179  		if err != nil {
   180  			return errors.Errorf("Failed to create namespace %s, err: %s", name, err)
   181  		}
   182  	}
   183  	return nil
   184  }
   185  
   186  func DeleteSecret(ns string, name string) error {
   187  	clientSet := kubernetes.NewForConfigOrDie(ctrl.GetConfigOrDie())
   188  	err := clientSet.CoreV1().Secrets(ns).Delete(context.Background(), name, metav1.DeleteOptions{})
   189  	if k8serr.IsNotFound(err) {
   190  		return nil
   191  	}
   192  	return err
   193  }
   194  
   195  func GetSecret(ns string, name string) (*v1.Secret, error) {
   196  	clientSet := kubernetes.NewForConfigOrDie(ctrl.GetConfigOrDie())
   197  	return clientSet.CoreV1().Secrets(ns).Get(context.Background(), name, metav1.GetOptions{})
   198  }
   199  
   200  func GetSecretValuesAsString(ns string, name string) (map[string]string, error) {
   201  	clientSet := kubernetes.NewForConfigOrDie(ctrl.GetConfigOrDie())
   202  	s, err := clientSet.CoreV1().Secrets(ns).Get(context.Background(), name, metav1.GetOptions{})
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  	result := make(map[string]string)
   207  	for k, v := range s.Data {
   208  		result[k] = string(v)
   209  	}
   210  	return result, nil
   211  }
   212  
   213  func CreateOrUpdateLicense(ns string, name string, license *infra.License) error {
   214  	k8sClient, err := client.New(config.GetConfigOrDie(), client.Options{
   215  		Scheme: ClientScheme,
   216  	})
   217  	if err != nil {
   218  		return err
   219  	}
   220  
   221  	if err = k8sClient.Get(context.Background(), client.ObjectKey{Namespace: ns, Name: name}, &infra.License{}); k8serr.IsNotFound(err) {
   222  		err = k8sClient.Create(context.Background(), license)
   223  		if err != nil {
   224  			return err
   225  		}
   226  	} else if err != nil {
   227  		return err
   228  	}
   229  	if err := k8sClient.Update(context.Background(), license); err != nil {
   230  		return err
   231  	}
   232  	return nil
   233  }
   234  
   235  type RESTClientGetter struct {
   236  	RestConfig *rest.Config
   237  }
   238  
   239  func (p RESTClientGetter) ToRawKubeConfigLoader() clientcmd.ClientConfig {
   240  	return nil
   241  }
   242  
   243  func (p RESTClientGetter) ToRESTConfig() (*rest.Config, error) {
   244  	return p.RestConfig, nil
   245  }
   246  
   247  func (p RESTClientGetter) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
   248  	home := homedir.HomeDir()
   249  	var httpCacheDir = filepath.Join(home, ".kube", "http-cache")
   250  	discoveryCacheDir := filepath.Join(home, ".kube", "cache", "discovery")
   251  	return disk.NewCachedDiscoveryClientForConfig(p.RestConfig, discoveryCacheDir, httpCacheDir, 10*time.Minute)
   252  }
   253  
   254  func (p RESTClientGetter) ToRESTMapper() (meta.RESTMapper, error) {
   255  	discoveryClient, _ := p.ToDiscoveryClient()
   256  	if discoveryClient != nil {
   257  		mapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient)
   258  		expander := restmapper.NewShortcutExpander(mapper, discoveryClient)
   259  		return expander, nil
   260  	}
   261  
   262  	return nil, fmt.Errorf("no restmapper")
   263  }