aboutsummaryrefslogtreecommitdiff
path: root/internal/k8s
diff options
context:
space:
mode:
authorXe Iaso <me@xeiaso.net>2024-11-09 10:50:59 -0500
committerGitHub <noreply@github.com>2024-11-09 10:50:59 -0500
commit20d07c7005665f8e2001b4cbd24e15ec589d9882 (patch)
tree8b98d1446c6d9e42bc2621e633543e0420f43541 /internal/k8s
parent2b349f56cb20420dd153f40f22b3654b5079100f (diff)
downloadxesite-20d07c7005665f8e2001b4cbd24e15ec589d9882.tar.xz
xesite-20d07c7005665f8e2001b4cbd24e15ec589d9882.zip
Move to Kubernetes (#853)
* start to lift-and-shift to k8s Signed-off-by: Xe Iaso <me@xeiaso.net> * manifest/xesite: properly configure pod disruption budget, hostmount for xesite as a hack Signed-off-by: Xe Iaso <me@xeiaso.net> * properly slonk readiness Signed-off-by: Xe Iaso <me@xeiaso.net> * manifest: move to aeacus Signed-off-by: Xe Iaso <me@xeiaso.net> * internal: add OnionLocation middleware Signed-off-by: Xe Iaso <me@xeiaso.net> * internal/lume: jettison serving from the zipfile Signed-off-by: Xe Iaso <me@xeiaso.net> * yolo deploy to prod Signed-off-by: Xe Iaso <me@xeiaso.net> * okay use a machineproxy here Signed-off-by: Xe Iaso <me@xeiaso.net> * test CI/CD Signed-off-by: Xe Iaso <me@xeiaso.net> * try civo route Signed-off-by: Xe Iaso <me@xeiaso.net> * lol Signed-off-by: Xe Iaso <me@xeiaso.net> * plan c? Signed-off-by: Xe Iaso <me@xeiaso.net> * specify the region Signed-off-by: Xe Iaso <me@xeiaso.net> * lol Signed-off-by: Xe Iaso <me@xeiaso.net> * blog: hello again kubernetes! Signed-off-by: Xe Iaso <me@xeiaso.net> --------- Signed-off-by: Xe Iaso <me@xeiaso.net>
Diffstat (limited to 'internal/k8s')
-rw-r--r--internal/k8s/k8s.go133
1 files changed, 133 insertions, 0 deletions
diff --git a/internal/k8s/k8s.go b/internal/k8s/k8s.go
new file mode 100644
index 0000000..433cffb
--- /dev/null
+++ b/internal/k8s/k8s.go
@@ -0,0 +1,133 @@
+package k8s
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "expvar"
+ "fmt"
+ "os"
+ "path/filepath"
+ "time"
+
+ "golang.org/x/oauth2"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/kubernetes"
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/tools/clientcmd"
+)
+
+const secretValue = "oauth2-token"
+
+var (
+ tokenRefreshCount = expvar.NewInt("gauge_xesite_k8s_token_refresh_count")
+
+ ErrSecretValueDoesntExist = errors.New("internal: can't find oauth2-token in secret")
+)
+
+type tokenSource struct {
+ secretName string
+ namespace string
+ base oauth2.TokenSource
+ clientSet kubernetes.Interface
+}
+
+func (kts *tokenSource) Token() (tok *oauth2.Token, err error) {
+ tok, _ = loadTokenFromK8s(context.Background(), kts.clientSet, kts.namespace, kts.secretName)
+
+ if tok != nil && tok.Expiry.After(time.Now()) {
+ return tok, nil
+ }
+
+ if tok, err = kts.base.Token(); err != nil {
+ return nil, err
+ }
+
+ tokenRefreshCount.Add(1)
+
+ if err := kts.saveToken(tok); err != nil {
+ return nil, err
+ }
+
+ return tok, err
+}
+
+func (kts *tokenSource) saveToken(tok *oauth2.Token) error {
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+
+ sec, err := kts.clientSet.CoreV1().Secrets(kts.namespace).Get(ctx, kts.secretName, v1.GetOptions{})
+ if err != nil {
+ return fmt.Errorf("can't get secret %s::%s: %w", kts.namespace, kts.secretName, err)
+ }
+
+ data, err := json.Marshal(tok)
+ if err != nil {
+ return fmt.Errorf("can't marshal json: %w", err)
+ }
+
+ sec.Data[secretValue] = data
+
+ if _, err := kts.clientSet.CoreV1().Secrets(kts.namespace).Update(ctx, sec, v1.UpdateOptions{}); err != nil {
+ return fmt.Errorf("can't update secret %s::%s: %w", kts.namespace, kts.secretName, err)
+ }
+
+ return nil
+}
+
+func loadTokenFromK8s(ctx context.Context, cs kubernetes.Interface, ns, secretName string) (*oauth2.Token, error) {
+ sec, err := cs.CoreV1().Secrets(ns).Get(ctx, secretName, v1.GetOptions{})
+ if err != nil {
+ return nil, fmt.Errorf("can't get secret %s::%s: %w", ns, secretName, err)
+ }
+
+ data, ok := sec.Data[secretValue]
+ if !ok {
+ return nil, ErrSecretValueDoesntExist
+ }
+
+ var tok oauth2.Token
+ if err := json.Unmarshal(data, &tok); err != nil {
+ return nil, fmt.Errorf("can't unmarshal oauth2-token in %s::%s: %w", ns, secretName, err)
+ }
+
+ return &tok, nil
+}
+
+func TokenSource(namespace string, secretName string, config *oauth2.Config) (oauth2.TokenSource, error) {
+ cs, err := getClientSet()
+ if err != nil {
+ return nil, fmt.Errorf("can't get client set: %w", err)
+ }
+
+ tok, err := loadTokenFromK8s(context.Background(), cs, namespace, secretName)
+ if err != nil {
+ return nil, err
+ }
+
+ orig := config.TokenSource(context.Background(), tok)
+
+ return oauth2.ReuseTokenSource(nil, &tokenSource{
+ secretName: secretName,
+ namespace: namespace,
+ base: orig,
+ clientSet: cs,
+ }), nil
+}
+
+func getClientSet() (kubernetes.Interface, error) {
+ config, err := rest.InClusterConfig()
+ if err != nil {
+ config, err = clientcmd.BuildConfigFromFlags("", filepath.Join(os.Getenv("HOME"), ".kube", "config"))
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ clientSet, err := kubernetes.NewForConfig(config)
+ if err != nil {
+ return nil, err
+ }
+
+ return clientSet, nil
+}