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 }