f1bcb8cd3a
repospawner turns JSON new-repo requests into terraform-git pull requests via kubernetes Jobs, follows those PRs to merge and optionally activates the repository in Woodpecker.
81 lines
2.5 KiB
Go
81 lines
2.5 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
|
|
batchv1 "k8s.io/api/batch/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/client-go/kubernetes"
|
|
"k8s.io/client-go/rest"
|
|
|
|
"git.unkin.net/unkin/repospawner/internal/jobs"
|
|
)
|
|
|
|
// Cluster is the slice of the Kubernetes API the server needs. Keeping it an
|
|
// interface lets the reconciler and handlers be tested without a cluster.
|
|
type Cluster interface {
|
|
// CreateJob creates a Job, reporting AlreadyExists as nil so a repeated
|
|
// reconcile is harmless.
|
|
CreateJob(ctx context.Context, job *batchv1.Job) error
|
|
// ListJobs returns every repospawner-owned Job in the namespace.
|
|
ListJobs(ctx context.Context) ([]batchv1.Job, error)
|
|
// ListPods returns every repospawner-owned Job pod in the namespace.
|
|
ListPods(ctx context.Context) ([]corev1.Pod, error)
|
|
// Ping reports whether the API server is reachable.
|
|
Ping(ctx context.Context) error
|
|
}
|
|
|
|
// KubeCluster is the in-cluster Cluster implementation.
|
|
type KubeCluster struct {
|
|
client kubernetes.Interface
|
|
namespace string
|
|
}
|
|
|
|
// NewKubeCluster builds a Cluster from the pod's in-cluster credentials.
|
|
func NewKubeCluster(namespace string) (*KubeCluster, error) {
|
|
cfg, err := rest.InClusterConfig()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
client, err := kubernetes.NewForConfig(cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &KubeCluster{client: client, namespace: namespace}, nil
|
|
}
|
|
|
|
// ownedSelector matches everything repospawner creates.
|
|
const ownedSelector = jobs.LabelApp + "=" + jobs.AppName + "," + jobs.LabelRequest
|
|
|
|
func (k *KubeCluster) CreateJob(ctx context.Context, job *batchv1.Job) error {
|
|
_, err := k.client.BatchV1().Jobs(k.namespace).Create(ctx, job, metav1.CreateOptions{})
|
|
if apierrors.IsAlreadyExists(err) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (k *KubeCluster) ListJobs(ctx context.Context) ([]batchv1.Job, error) {
|
|
list, err := k.client.BatchV1().Jobs(k.namespace).List(ctx, metav1.ListOptions{LabelSelector: ownedSelector})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return list.Items, nil
|
|
}
|
|
|
|
func (k *KubeCluster) ListPods(ctx context.Context) ([]corev1.Pod, error) {
|
|
list, err := k.client.CoreV1().Pods(k.namespace).List(ctx, metav1.ListOptions{LabelSelector: ownedSelector})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return list.Items, nil
|
|
}
|
|
|
|
func (k *KubeCluster) Ping(ctx context.Context) error {
|
|
limit := int64(1)
|
|
_, err := k.client.BatchV1().Jobs(k.namespace).List(ctx, metav1.ListOptions{Limit: limit})
|
|
return err
|
|
}
|