summaryrefslogtreecommitdiffstats
path: root/pkg/deployer/instance.go
diff options
context:
space:
mode:
authorLeonardo Bishop <me@leonardobishop.net>2026-01-07 23:39:53 +0000
committerLeonardo Bishop <me@leonardobishop.net>2026-01-07 23:39:53 +0000
commit03cd6bdfbd473dba3f3dc50a1b15e389aac5bc70 (patch)
tree5fea2b1840e298aaab953add749fb9226bd4a710 /pkg/deployer/instance.go
Initial commit
Diffstat (limited to 'pkg/deployer/instance.go')
-rw-r--r--pkg/deployer/instance.go163
1 files changed, 163 insertions, 0 deletions
diff --git a/pkg/deployer/instance.go b/pkg/deployer/instance.go
new file mode 100644
index 0000000..99171f4
--- /dev/null
+++ b/pkg/deployer/instance.go
@@ -0,0 +1,163 @@
+package deployer
+
+import (
+ "context"
+ "fmt"
+ "log/slog"
+ "strconv"
+ "time"
+
+ "github.com/moby/moby/client"
+)
+
+type Instance struct {
+ ChallengeName string
+ DeployKey string
+ Address string
+ AddressFormat string
+ ExpiresAt time.Time
+}
+
+func (d *DockerDeployer) GetTeamInstances(ctx context.Context, team string) ([]Instance, error) {
+ filters := client.Filters{}
+ filters.Add("label", ContainerLabelForTeam+"="+team)
+
+ containers, err := d.client.ContainerList(ctx, client.ContainerListOptions{
+ All: true,
+ Filters: filters,
+ })
+ if err != nil {
+ return []Instance{}, err
+ }
+
+ var instances []Instance
+ for _, c := range containers.Items {
+ expiresAt, err := strconv.Atoi(c.Labels[ContainerLabelExpiresAt])
+ if err != nil {
+ slog.Error("container has invalid expiry", "container", c.ID, "expiry", c.Labels[ContainerLabelExpiresAt])
+ continue
+ }
+ instances = append(instances, Instance{
+ ChallengeName: c.Labels[ContainerLabelChallenge],
+ DeployKey: c.Labels[ContainerLabelDeployKey],
+ Address: c.Labels[ContainerLabelAddress],
+ AddressFormat: c.Labels[ContainerLabelAddressFormat],
+ ExpiresAt: time.Unix(int64(expiresAt), 0),
+ })
+ }
+ return instances, nil
+}
+
+func (d *DockerDeployer) StopInstance(ctx context.Context, deployKey, team string) error {
+ if deployKey == "" || team == "" {
+ return fmt.Errorf("deploy key/team is invalid")
+ }
+
+ filters := client.Filters{}
+ filters.Add("label", ContainerLabelForTeam+"="+team)
+ filters.Add("label", ContainerLabelDeployKey+"="+deployKey)
+
+ containers, err := d.client.ContainerList(ctx, client.ContainerListOptions{
+ All: true,
+ Filters: filters,
+ })
+ if err != nil {
+ return fmt.Errorf("docker error")
+ }
+
+ if len(containers.Items) == 0 {
+ return fmt.Errorf("no such instance")
+ }
+
+ for _, c := range containers.Items {
+ _, err := d.client.ContainerRemove(ctx, c.ID, client.ContainerRemoveOptions{
+ Force: true,
+ })
+ if err != nil {
+ return fmt.Errorf("docker error")
+ }
+ slog.Info("container removed early", "container", c.ID)
+ }
+
+ networks, err := d.client.NetworkList(ctx, client.NetworkListOptions{})
+ if err != nil {
+ return fmt.Errorf("docker error")
+ }
+ for _, n := range networks.Items {
+ if err = d.forceRemoveNetwork(ctx, n.ID); err != nil {
+ slog.Warn("failed to remove network", "network", n.ID)
+ continue
+ }
+ slog.Info("network removed early", "network", n.ID)
+ }
+
+ return nil
+}
+
+func (d *DockerDeployer) RemoveExpiredResources(ctx context.Context) error {
+ filters := client.Filters{}
+ filters.Add("label", ContainerLabelManaged+"=yes")
+
+ containers, err := d.client.ContainerList(ctx, client.ContainerListOptions{
+ All: true,
+ Filters: filters,
+ })
+ if err != nil {
+ return err
+ }
+ for _, c := range containers.Items {
+ expiry, err := strconv.ParseInt(c.Labels[ContainerLabelExpiresAt], 10, 64)
+ if err != nil {
+ slog.Warn("invalid timestamp on container label", "container", c.ID, "timestamp", c.Labels[ContainerLabelExpiresAt])
+ continue
+ }
+ if expiry > time.Now().Unix() {
+ continue
+ }
+
+ _, err = d.client.ContainerRemove(ctx, c.ID, client.ContainerRemoveOptions{
+ Force: true,
+ })
+ if err != nil {
+ return err
+ }
+ slog.Info("expired container removed", "container", c.ID)
+ }
+
+ networks, err := d.client.NetworkList(ctx, client.NetworkListOptions{
+ Filters: filters,
+ })
+ if err != nil {
+ return err
+ }
+ for _, n := range networks.Items {
+ expiry, err := strconv.ParseInt(n.Labels[ContainerLabelExpiresAt], 10, 64)
+ if err != nil {
+ slog.Warn("invalid timestamp on network label", "network", n.ID, "timestamp", n.Labels[ContainerLabelExpiresAt])
+ continue
+ }
+ if expiry > time.Now().Unix() {
+ continue
+ }
+
+ if err = d.forceRemoveNetwork(ctx, n.ID); err != nil {
+ return err
+ }
+ slog.Info("expired network removed", "network", n.ID)
+ }
+
+ return nil
+}
+
+func (d *DockerDeployer) forceRemoveNetwork(ctx context.Context, networkID string) error {
+ _, _ = d.client.NetworkDisconnect(ctx, networkID, client.NetworkDisconnectOptions{
+ Container: d.proxyContainerName,
+ Force: true,
+ })
+
+ _, err := d.client.NetworkRemove(ctx, networkID, client.NetworkRemoveOptions{})
+ if err != nil {
+ return err
+ }
+ return nil
+}