This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / cmd / spindle / main.go
5.2 kB 203 lines
1package main 2 3import ( 4 "context" 5 "fmt" 6 "log/slog" 7 "os" 8 "strings" 9 "text/tabwriter" 10 "time" 11 12 "github.com/urfave/cli/v3" 13 tlog "tangled.org/core/log" 14 "tangled.org/core/spindle" 15 "tangled.org/core/spindle/db" 16 "tangled.org/core/spindle/mill" 17) 18 19func main() { 20 cmd := &cli.Command{ 21 Name: "spindle", 22 Usage: "spindle continuous integration runner", 23 Commands: []*cli.Command{ 24 Command(), 25 millCommand(), 26 }, 27 DefaultCommand: "run", 28 } 29 30 logger := tlog.New("spindle") 31 slog.SetDefault(logger) 32 33 ctx := context.Background() 34 ctx = tlog.IntoContext(ctx, logger) 35 36 if err := cmd.Run(ctx, os.Args); err != nil { 37 logger.Error(err.Error()) 38 os.Exit(-1) 39 } 40} 41 42func Command() *cli.Command { 43 return &cli.Command{ 44 Name: "run", 45 Usage: "run the spindle server", 46 Action: func(ctx context.Context, cmd *cli.Command) error { 47 return spindle.Run(ctx) 48 }, 49 } 50} 51 52// for mill host administration and executor management 53func millCommand() *cli.Command { 54 dbFlag := &cli.StringFlag{ 55 Name: "db", 56 Usage: "path to the spindle sqlite db", 57 Value: "spindle.db", 58 Sources: cli.EnvVars("SPINDLE_SERVER_DB_PATH"), 59 } 60 openDB := func(ctx context.Context, cmd *cli.Command) (*db.DB, error) { 61 return db.Make(ctx, cmd.String("db")) 62 } 63 return &cli.Command{ 64 Name: "mill", 65 Usage: "mill host administration", 66 Commands: []*cli.Command{ 67 { 68 Name: "executor", 69 Usage: "manage executors allowed to join this mill", 70 Commands: []*cli.Command{ 71 { 72 Name: "add", 73 Usage: "register an executor and print its token", 74 ArgsUsage: "<name>", 75 Flags: []cli.Flag{ 76 dbFlag, 77 &cli.DurationFlag{ 78 Name: "ttl", 79 Usage: "token lifetime (e.g. 720h); omit for no expiry", 80 }, 81 &cli.StringSliceFlag{ 82 Name: "label", 83 Usage: "authorized labels for this executor", 84 }, 85 }, 86 Action: func(ctx context.Context, cmd *cli.Command) error { 87 name := cmd.Args().First() 88 if name == "" { 89 return fmt.Errorf("usage: spindle mill executor add <name>") 90 } 91 d, err := openDB(ctx, cmd) 92 if err != nil { 93 return err 94 } 95 token, err := mill.GenerateToken() 96 if err != nil { 97 return err 98 } 99 var expiresAt *time.Time 100 if ttl := cmd.Duration("ttl"); ttl > 0 { 101 exp := time.Now().UTC().Add(ttl) 102 expiresAt = &exp 103 } 104 labels := cmd.StringSlice("label") 105 if err := d.AddExecutorToken(name, mill.HashToken(token), expiresAt, labels); err != nil { 106 return fmt.Errorf("registering executor %q: %w", name, err) 107 } 108 fmt.Println(token) 109 return nil 110 }, 111 }, 112 { 113 Name: "list", 114 Usage: "list registered executors", 115 Flags: []cli.Flag{dbFlag}, 116 Action: func(ctx context.Context, cmd *cli.Command) error { 117 d, err := openDB(ctx, cmd) 118 if err != nil { 119 return err 120 } 121 tokens, err := d.ListExecutorTokens() 122 if err != nil { 123 return err 124 } 125 w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) 126 fmt.Fprintln(w, "NAME\tCREATED\tEXPIRES\tLABELS\tQUARANTINE") 127 for _, t := range tokens { 128 expires := "never" 129 if t.ExpiresAt != nil { 130 expires = t.ExpiresAt.Format(time.RFC3339) 131 if time.Now().After(*t.ExpiresAt) { 132 expires += " (expired)" 133 } 134 } 135 labels := strings.Join(t.Labels, ",") 136 if labels == "" { 137 labels = "-" 138 } 139 quarantine := "-" 140 if t.QuarantineReason != nil { 141 quarantine = *t.QuarantineReason 142 if t.QuarantinedAt != nil { 143 quarantine = *t.QuarantinedAt + ": " + quarantine 144 } 145 } 146 fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", t.Name, t.CreatedAt, expires, labels, quarantine) 147 } 148 return w.Flush() 149 }, 150 }, 151 { 152 Name: "unquarantine", 153 Usage: "allow a quarantined executor to reconnect", 154 ArgsUsage: "<name>", 155 Flags: []cli.Flag{dbFlag}, 156 Action: func(ctx context.Context, cmd *cli.Command) error { 157 name := cmd.Args().First() 158 if name == "" { 159 return fmt.Errorf("usage: spindle mill executor unquarantine <name>") 160 } 161 d, err := openDB(ctx, cmd) 162 if err != nil { 163 return err 164 } 165 ok, err := d.ClearExecutorQuarantine(name) 166 if err != nil { 167 return err 168 } 169 if !ok { 170 return fmt.Errorf("no such executor identity %q", name) 171 } 172 return nil 173 }, 174 }, 175 { 176 Name: "revoke", 177 Usage: "revoke an executor's token", 178 ArgsUsage: "<name>", 179 Flags: []cli.Flag{dbFlag}, 180 Action: func(ctx context.Context, cmd *cli.Command) error { 181 name := cmd.Args().First() 182 if name == "" { 183 return fmt.Errorf("usage: spindle mill executor revoke <name>") 184 } 185 d, err := openDB(ctx, cmd) 186 if err != nil { 187 return err 188 } 189 ok, err := d.RevokeExecutorToken(name) 190 if err != nil { 191 return err 192 } 193 if !ok { 194 return fmt.Errorf("no such executor identity %q", name) 195 } 196 return nil 197 }, 198 }, 199 }, 200 }, 201 }, 202 } 203}