Add the initial repospawner service
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.
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user