diff options
| author | Xe Iaso <me@xeiaso.net> | 2023-10-27 21:02:10 -0400 |
|---|---|---|
| committer | Xe Iaso <me@xeiaso.net> | 2023-10-27 21:02:14 -0400 |
| commit | 334992d356f1f779043fcfe9e6d8257c4a3a9e63 (patch) | |
| tree | bc612b036be840bee49350f695a5278fcbd4ef9c /cmd | |
| parent | e5551312962cf1179ceb57a7fbeddaba55a2eab2 (diff) | |
| download | xesite-334992d356f1f779043fcfe9e6d8257c4a3a9e63.tar.xz xesite-334992d356f1f779043fcfe9e6d8257c4a3a9e63.zip | |
cmd/fabricate-generation: XeDN uploading tool for xesite v4
This tool will create new xesite generations for XeDN and upload them to
the XeDN nodes as it is created. This is intended to be invoked via CI.
Signed-off-by: Xe Iaso <me@xeiaso.net>
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/fabricate-generation/main.go | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/cmd/fabricate-generation/main.go b/cmd/fabricate-generation/main.go new file mode 100644 index 0000000..3b658b6 --- /dev/null +++ b/cmd/fabricate-generation/main.go @@ -0,0 +1,180 @@ +package main + +import ( + "context" + "flag" + "log" + "log/slog" + "net/http" + "os" + "sync" + + "github.com/facebookgo/flagenv" + _ "github.com/joho/godotenv/autoload" + "golang.org/x/oauth2" + "golang.org/x/oauth2/clientcredentials" + "gopkg.in/mxpv/patreon-go.v1" + "tailscale.com/client/tailscale" + "tailscale.com/hostinfo" + "tailscale.com/ipn/store/mem" + "tailscale.com/tsnet" + "tailscale.com/util/cmpx" + "within.website/x/web" + "xeiaso.net/v4/internal" + "xeiaso.net/v4/internal/lume" + "xeiaso.net/v4/internal/saasproxytoken" +) + +var ( + githubSHA = flag.String("github-sha", "", "GitHub SHA to use for the site") + miToken = flag.String("mi-token", "", "Token to use for the mi API") + patreonSaasProxyURL = flag.String("patreon-saasproxy-url", "http://patreon-saasproxy/give-token", "URL to use for the patreon saasproxy") + tailscaleClientID = flag.String("tailscale-client-id", "", "Tailscale client ID to use") + tailscaleClientSecret = flag.String("tailscale-client-secret", "", "Tailscale client secret to use") +) + +func main() { + // Required to use the Tailscale client API. This is sussy, but okay. + tailscale.I_Acknowledge_This_API_Is_Unstable = true + + flagenv.Parse() + flag.Parse() + internal.Slog() + + baseURL := cmpx.Or(os.Getenv("TS_BASE_URL"), "https://api.tailscale.com") + + credentials := clientcredentials.Config{ + ClientID: *tailscaleClientID, + ClientSecret: *tailscaleClientSecret, + TokenURL: baseURL + "/api/v2/oauth/token", + Scopes: []string{"device"}, + } + + ctx := context.Background() + tsClient := tailscale.NewClient("-", nil) + tsClient.HTTPClient = credentials.Client(ctx) + tsClient.BaseURL = baseURL + + caps := tailscale.KeyCapabilities{ + Devices: tailscale.KeyDeviceCapabilities{ + Create: tailscale.KeyDeviceCreateCapabilities{ + Reusable: false, + Ephemeral: true, + Preauthorized: true, + Tags: []string{"tag:service", "tag:ci"}, + }, + }, + } + + authkey, _, err := tsClient.CreateKey(ctx, caps) + if err != nil { + log.Fatal(err.Error()) + } + + os.Args[0] = "via XeDN" + + hostinfo.SetApp("xeiaso.net/v4/cmd/fabricate-generation") + + memStore, err := mem.New(log.Printf, "") + if err != nil { + log.Fatal(err) + } + + srv := &tsnet.Server{ + Hostname: "github-action-" + (*githubSHA)[:7], + Logf: log.Printf, + Ephemeral: true, + Store: memStore, + AuthKey: authkey, + } + + if err := srv.Start(); err != nil { + log.Fatal(err) + } + + if _, err := srv.Up(context.Background()); err != nil { + log.Fatal(err) + } + + hc := srv.HTTPClient() + + pc, err := NewPatreonClient(hc) + if err != nil { + slog.Error("can't create patreon client", "err", err) + } + + os.MkdirAll("./var", 0700) + + fs, err := lume.New(context.Background(), &lume.Options{ + Branch: "main", + Repo: "https://github.com/Xe/site", + StaticSiteDir: "lume", + URL: "https://xeiaso.net", + Development: false, + PatreonClient: pc, + DataDir: "./var", + MiToken: *miToken, + }) + if err != nil { + log.Fatal(err) + } + + defer fs.Close() + + var wg sync.WaitGroup + + for _, region := range []string{"fra", "sea", "yyz"} { + wg.Add(1) + go func(region string) { + defer wg.Done() + + if err := uploadSlug(hc, "xedn-"+region, "./var/site.zip"); err != nil { + slog.Error("error updating", "region", region, "error", err) + } + }(region) + } + + wg.Wait() +} + +func uploadSlug(cli *http.Client, host, fname string) error { + fin, err := os.Open(fname) + if err != nil { + return err + } + defer fin.Close() + + req, err := http.NewRequest("PUT", "http://"+host+"/xesite/upload", fin) + if err != nil { + return err + } + + slog.Info("uploading", "host", host) + + resp, err := cli.Do(req) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + return web.NewError(http.StatusOK, resp) + } + + slog.Info("done", "host", host) + + return nil +} + +func NewPatreonClient(hc *http.Client) (*patreon.Client, error) { + ts := saasproxytoken.RemoteTokenSource(*patreonSaasProxyURL, hc) + tc := oauth2.NewClient(context.Background(), ts) + + client := patreon.NewClient(tc) + if u, err := client.FetchUser(); err != nil { + return nil, err + } else { + slog.Info("logged in as", "user", u.Data.Attributes.FullName) + } + + return client, nil +} |
