aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorXe Iaso <me@xeiaso.net>2023-10-27 21:02:10 -0400
committerXe Iaso <me@xeiaso.net>2023-10-27 21:02:14 -0400
commit334992d356f1f779043fcfe9e6d8257c4a3a9e63 (patch)
treebc612b036be840bee49350f695a5278fcbd4ef9c /cmd
parente5551312962cf1179ceb57a7fbeddaba55a2eab2 (diff)
downloadxesite-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.go180
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
+}