Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package main
- import (
- "bytes"
- "database/sql"
- "encoding/base64"
- "log"
- "os"
- "strings"
- "text/template"
- _ "github.com/lib/pq"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/client-go/kubernetes"
- "k8s.io/client-go/pkg/api/v1"
- "k8s.io/client-go/rest"
- )
- // How to use this script:
- //
- // Create a deployment which depends on an RDS password and have a master RDS
- // password loaded as a secret.
- //
- // This script will given a version (v1):
- // 1. Create a new user with whatever role you'd like named `myuser_v1`.
- // 2. Create a secret `myapp-v1` with the password.
- // 3. Your app uses secret myapp-v1 and connects to the DB.
- //
- // Rolling the database password:
- // 1. Bump the version on this script to v2.
- // 2. Same user + new secret are created, your app is configured to use the v2
- // user.
- // 3. Importantly - now the old script will delete user v1 once no connections
- // are currently active with this username.
- const createUser = `
- create role {{.User}} with password '{{.Password}}' login;
- grant {{.Role}} to {{.User}};
- `
- func tpl(m map[string]string, tplStr string) (string, error) {
- t, err := template.New("").Parse(tplStr)
- if err != nil {
- return "", err
- }
- buf := bytes.NewBuffer(nil)
- err = t.Execute(buf, m)
- if err != nil {
- return "", err
- }
- return buf.String(), nil
- }
- func base64Enc(pass string) []byte {
- return []byte(base64.StdEncoding.EncodeToString([]byte(pass)))
- }
- func main() {
- url := os.Getenv("DATABASE_URL")
- revision := os.Getenv("DATABASE_REVISION")
- user := os.Getenv("DATABASE_USER")
- password := os.Getenv("DATABASE_PASSWORD")
- role := os.Getenv("DATABASE_ROLE")
- namespace := os.Getenv("NAMESPACE")
- secret := os.Getenv("SECRET")
- config, err := rest.InClusterConfig()
- if err != nil {
- log.Fatalf("failed to open k8s client: %v", err)
- }
- clientset, err := kubernetes.NewForConfig(config)
- if err != nil {
- log.Fatalf("failed to open k8s client: %v", err)
- }
- db, err := sql.Open("postgres", url)
- if err != nil {
- log.Fatalf("failed to open: %v", err)
- }
- sql, err := tpl(map[string]string{
- "User": user + "_" + revision,
- "Password": password,
- "Role": role,
- }, createUser)
- if err != nil {
- log.Fatalf("failed to template: %v", err)
- }
- // 1. Create a new user.
- _, err = db.Exec(sql)
- if err != nil {
- log.Fatalf("failed to exec: %v", err)
- }
- // 2. Create a new secret for the given revision.
- _, err = clientset.CoreV1().Secrets(namespace).Create(&v1.Secret{
- ObjectMeta: metav1.ObjectMeta{
- Namespace: namespace,
- Name: secret + "-" + revision,
- },
- Data: map[string][]byte{
- "password": base64Enc(password),
- },
- })
- if err != nil {
- log.Fatalf("failed to create secret: %v", err)
- }
- for {
- secrets, err := clientset.CoreV1().Secrets(namespace).List(metav1.ListOptions{})
- if err != nil {
- log.Fatalf("failed to list secrets: %v", err)
- }
- for _, s := range secrets.Items {
- if strings.HasPrefix(s.ObjectMeta.Name, secret) &&
- s.ObjectMeta.Name != secret+"-"+revision {
- currentRevSpl := strings.Split(s.ObjectMeta.Name, "-")
- currentRev := currentRevSpl[len(currentRevSpl)-1]
- // Check if isn't currently used.
- db.Query("SELECT 1 from pg_stat_activity WHERE usename = ?", user+"_"+currentRev)
- }
- }
- }
- }
Add Comment
Please, Sign In to add comment