-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathflynn_client.go
127 lines (106 loc) · 2.92 KB
/
flynn_client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package main
import (
"encoding/base64"
"fmt"
"io"
"os"
"github.com/flynn/flynn/controller/client"
ct "github.com/flynn/flynn/controller/types"
"github.com/flynn/flynn/pkg/cluster"
)
type FlynnClient struct {
client controller.Client
}
type AppAndRelease struct {
App *ct.App
Release *ct.Release
}
func NewFlynnClient() (*FlynnClient, error) {
// get config from env vars
controllerUrl := os.Getenv("CONTROLLER_URL")
if controllerUrl == "" {
controllerUrl = "http://controller.discoverd"
}
controllerKey := os.Getenv("CONTROLLER_KEY")
controllerTlsPin := os.Getenv("CONTROLLER_TLS_PIN")
var c controller.Client
var err error
if controllerTlsPin == "" {
c, err = controller.NewClient(controllerUrl, controllerKey)
if err != nil {
return nil, err
}
} else {
pin, err := base64.StdEncoding.DecodeString(controllerTlsPin)
if err != nil {
return nil, err
}
c, err = controller.NewClientWithConfig(controllerUrl, controllerKey, controller.Config{Pin: pin})
if err != nil {
return nil, err
}
}
return &FlynnClient{client: c}, nil
}
func (c *FlynnClient) GetApp(name string) (*ct.App, error) {
return c.client.GetApp(name)
}
func (c *FlynnClient) AppList() ([]*AppAndRelease, error) {
allApps, err := c.client.AppList()
if err != nil {
return nil, err
}
result := []*AppAndRelease{}
for _, a := range allApps {
r, _ := c.client.GetAppRelease(a.ID)
// identify apps to backup by FLYNN_POSTGRES env var existing
if r.Env["FLYNN_POSTGRES"] != "" {
result = append(result, &AppAndRelease{App: a, Release: r})
}
}
return result, nil
}
func (c *FlynnClient) StreamBackup(app *AppAndRelease, w io.Writer) error {
req, err := c.createPgBackupJobRequest(app)
if err != nil {
return err
}
rwc, err := c.client.RunJobAttached(app.App.ID, req)
if err != nil {
return err
}
defer rwc.Close()
attachClient := cluster.NewAttachClient(rwc)
attachClient.CloseWrite()
// not worried about exit status...?
_, err = attachClient.Receive(w, os.Stderr)
return err
}
func (c *FlynnClient) createPgBackupJobRequest(app *AppAndRelease) (*ct.NewJob, error) {
// from: https://github.com/flynn/flynn/blob/master/cli/pg.go
pgApp := app.Release.Env["FLYNN_POSTGRES"]
if pgApp == "" {
return nil, fmt.Errorf("no postgres database found.")
}
// TODO: this pgRelease is likely shared by all/most the apps. cache result
pgRelease, err := c.client.GetAppRelease(pgApp)
if err != nil {
return nil, fmt.Errorf("error getting postgres release: %s", err)
}
req := &ct.NewJob{
Args: []string{"pg_dump", "--format=custom", "--no-owner", "--no-acl"},
TTY: false,
ReleaseID: pgRelease.ID,
ReleaseEnv: false,
Env: make(map[string]string),
DisableLog: true,
}
for _, k := range []string{"PGHOST", "PGUSER", "PGPASSWORD", "PGDATABASE"} {
v := app.Release.Env[k]
if v == "" {
return nil, fmt.Errorf("missing %s in app environment", k)
}
req.Env[k] = v
}
return req, nil
}