diff options
| author | Xe Iaso <me@xeiaso.net> | 2024-11-09 10:50:59 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-11-09 10:50:59 -0500 |
| commit | 20d07c7005665f8e2001b4cbd24e15ec589d9882 (patch) | |
| tree | 8b98d1446c6d9e42bc2621e633543e0420f43541 /internal/k8s | |
| parent | 2b349f56cb20420dd153f40f22b3654b5079100f (diff) | |
| download | xesite-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.go | 133 |
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 +} |
