| package k8s |
| |
| import ( |
| "context" |
| |
| appsv1 "k8s.io/api/apps/v1" |
| corev1 "k8s.io/api/core/v1" |
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| "k8s.io/client-go/kubernetes" |
| "k8s.io/client-go/rest" |
| "k8s.io/client-go/tools/clientcmd" |
| "k8s.io/client-go/tools/clientcmd/api" |
| |
| "go.skia.org/infra/go/kube/clusterconfig" |
| "go.skia.org/infra/go/skerr" |
| "go.skia.org/infra/go/sklog" |
| ) |
| |
| // Client provides an interface for interacting with a Kubernetes cluster. It is |
| // a thin wrapper around the Kubernetes API which allows for mocking requests. |
| type Client interface { |
| // ListNamespaces retrieves all namespaces in the cluster. |
| ListNamespaces(ctx context.Context, opts metav1.ListOptions) ([]corev1.Namespace, error) |
| |
| // ListPods retrieves all pods in the namespace. |
| ListPods(ctx context.Context, namespace string, opts metav1.ListOptions) ([]corev1.Pod, error) |
| |
| // DeletePod deletes a pod. |
| DeletePod(ctx context.Context, namespace, name string, opts metav1.DeleteOptions) error |
| |
| // ListStatefulSets retrieves all stateful sets in the namespace. |
| ListStatefulSets(ctx context.Context, namespace string, opts metav1.ListOptions) ([]appsv1.StatefulSet, error) |
| |
| // GetStatefulSet retrieves a single StatefulSet. |
| GetStatefulSet(ctx context.Context, namespace, name string, opts metav1.GetOptions) (*appsv1.StatefulSet, error) |
| |
| // GetEvents retrieves events for the given namespace. |
| GetEvents(ctx context.Context, namespace string) ([]corev1.Event, error) |
| } |
| |
| // ClientImpl implements Client. |
| type ClientImpl struct { |
| c *kubernetes.Clientset |
| } |
| |
| // NewClient returns a ClientImpl instance. |
| // TODO(borenet): Handle more of the setup here. |
| func NewClient(ctx context.Context, clientset *kubernetes.Clientset) (*ClientImpl, error) { |
| return &ClientImpl{ |
| c: clientset, |
| }, nil |
| } |
| |
| // GetNamespaces implements Client. |
| func (c *ClientImpl) ListNamespaces(ctx context.Context, opts metav1.ListOptions) ([]corev1.Namespace, error) { |
| result, err := c.c.CoreV1().Namespaces().List(ctx, opts) |
| if err != nil { |
| return nil, skerr.Wrap(err) |
| } |
| return result.Items, nil |
| } |
| |
| // GetPods implements Client. |
| func (c *ClientImpl) ListPods(ctx context.Context, namespace string, opts metav1.ListOptions) ([]corev1.Pod, error) { |
| result, err := c.c.CoreV1().Pods(namespace).List(ctx, opts) |
| if err != nil { |
| return nil, skerr.Wrap(err) |
| } |
| return result.Items, nil |
| } |
| |
| // DeletePod implements Client. |
| func (c *ClientImpl) DeletePod(ctx context.Context, namespace, name string, opts metav1.DeleteOptions) error { |
| return skerr.Wrap(c.c.CoreV1().Pods(namespace).Delete(ctx, name, opts)) |
| } |
| |
| // ListStatefulSets implements Client. |
| func (c *ClientImpl) ListStatefulSets(ctx context.Context, namespace string, opts metav1.ListOptions) ([]appsv1.StatefulSet, error) { |
| result, err := c.c.AppsV1().StatefulSets(namespace).List(ctx, opts) |
| if err != nil { |
| return nil, skerr.Wrap(err) |
| } |
| return result.Items, nil |
| } |
| |
| // GetStatefulSet implements Client. |
| func (c *ClientImpl) GetStatefulSet(ctx context.Context, namespace, name string, opts metav1.GetOptions) (*appsv1.StatefulSet, error) { |
| result, err := c.c.AppsV1().StatefulSets(namespace).Get(ctx, name, metav1.GetOptions{}) |
| if err != nil { |
| return nil, skerr.Wrap(err) |
| } |
| return result, nil |
| } |
| |
| // GetEvents implements Client. |
| func (c *ClientImpl) GetEvents(ctx context.Context, namespace string) ([]corev1.Event, error) { |
| resp, err := c.c.CoreV1().Events(namespace).List(ctx, metav1.ListOptions{}) |
| if err != nil { |
| return nil, skerr.Wrap(err) |
| } |
| return resp.Items, nil |
| } |
| |
| // Assert that ClientImpl implements Client. |
| var _ Client = &ClientImpl{} |
| |
| // NewInClusterClient creates a Client which can be used to interact with the |
| // cluster in which this service is running. |
| func NewInClusterClient(ctx context.Context) (*ClientImpl, error) { |
| config, err := rest.InClusterConfig() |
| if err != nil { |
| return nil, skerr.Wrapf(err, "failed to get in-cluster config") |
| } |
| sklog.Infof("Auth username: %s", config.Username) |
| clientset, err := kubernetes.NewForConfig(config) |
| if err != nil { |
| return nil, skerr.Wrapf(err, "failed to get in-cluster clientset") |
| } |
| return NewClient(ctx, clientset) |
| } |
| |
| // NewLocalClient uses the local kubeconfig file, the clusters config.json file, |
| // and a cluster name to create a Client which can be used to interact with that |
| // cluster. |
| // TODO(borenet): This requires that the user has previously created credentials |
| // for the requested cluster. |
| func NewLocalClient(ctx context.Context, kubeConfigFile, clusterConfigFile, cluster string) (*ClientImpl, error) { |
| clusterConfig, err := clusterconfig.New(clusterConfigFile) |
| if err != nil { |
| return nil, skerr.Wrap(err) |
| } |
| clusterInfo, ok := clusterConfig.Clusters[cluster] |
| if !ok { |
| return nil, skerr.Fmt("unknown cluster %q", cluster) |
| } |
| cfg, err := clientcmd.LoadFromFile(kubeConfigFile) |
| if err != nil { |
| return nil, skerr.Wrap(err) |
| } |
| cfg.CurrentContext = clusterInfo.ContextName |
| clusterCfg, ok := cfg.Clusters[clusterInfo.ContextName] |
| if !ok { |
| return nil, skerr.Fmt("no cluster config for %q", clusterInfo.ContextName) |
| } |
| restCfg, err := clientcmd.BuildConfigFromKubeconfigGetter(clusterCfg.Server, func() (*api.Config, error) { |
| return cfg, nil |
| }) |
| if err != nil { |
| return nil, skerr.Wrap(err) |
| } |
| clientset, err := kubernetes.NewForConfig(restCfg) |
| if err != nil { |
| return nil, skerr.Wrap(err) |
| } |
| return NewClient(ctx, clientset) |
| } |