# Fleeting

Fleeting is an abstraction for cloud providers' instance groups. It allows for the provisioning of multiple identical
instances with a minimal API focused on just creation, connection and deletion.

## Usage

```golang
package main

import (
	"context"
	"fmt"
	"os"
	"time"

	"gitlab.com/gitlab-org/fleeting/fleeting"
	googlecompute "gitlab.com/gitlab-org/fleeting/fleeting-plugin-googlecompute"
	"gitlab.com/gitlab-org/fleeting/fleeting/connector"
	"gitlab.com/gitlab-org/fleeting/fleeting/provider"
)

func main() {
	group := &googlecompute.InstanceGroup{
		CredentialsFile: "credentials.json",
		Project:         "name-of-my-project",
		Zone:            "europe-west4-b",
		Name:            "name-of-my-instance-group",
	}

	settings := provider.Settings{}
	settings.Username = "core"

	subCh := make(chan fleeting.Instance, 10)
	opts := []fleeting.Option{
		fleeting.WithMaxSize(10),
		fleeting.WithInstanceGroupSettings(settings),

		// subscribe to instance updates
		fleeting.WithSubscriber(func(instances []fleeting.Instance) {
			for _, inst := range instances {
				subCh <- inst
			}
		}),
	}

	provisioner, err := fleeting.Init(context.Background(), nil, group, opts...)
	if err != nil {
		panic(err)
	}

	defer provisioner.Shutdown(context.Background())

	// request an instance
	provisioner.Request(1)

	// only run provisioner and subscribe to updates for 2 minutes
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
	defer cancel()

	for {
		select {
		case <-ctx.Done():
			return

		case instance := <-subCh:
			fmt.Println("Instance:", instance.ID(), "State:", instance.State(), "Cause:", instance.Cause())

			// ignore pre-existing instances (ones we haven't requested)
			if instance.Cause() == fleeting.CausePreexisted {
				continue
			}

			// ignore if the instance is not running
			if instance.State() != provider.StateRunning {
				continue
			}

			// wait for instance to be running and connect to it
			info, err := instance.ConnectInfo(context.Background())
			if err != nil {
				panic(err)
			}

			err = connector.Run(context.Background(), info, connector.ConnectorOptions{
				RunOptions: connector.RunOptions{
					Command: "echo 'hello world'",
					Stdout:  os.Stdout,
					Stderr:  os.Stderr,
				},
				UseExternalAddr: true,
			})
			if err != nil {
				panic(err)
			}

			// delete the instance
			instance.Delete()
		}
	}
}
```

### Plugin support

Fleeting also supports plugins:

```golang
	runner, err := fleeting.RunPlugin("fleeting-plugin-googlecompute", []byte(`{
		"credentials_file": "credentials.json",
		"project":          "name-of-my-project",
		"zone":             "europe-west4-b",
		"name":             "name-of-my-instance-group"
	}`))
	if err != nil {
		return err
	}
	defer runner.Kill()

	provisioner, err := fleeting.New(nil, runner.InstanceGroup(), settings)
	if err != nil {
		return err
	}
```

