~egtann/terrafirma

d016b3cce430f75eb8471fea5ab094b4da20a18f — Evan Tann 6 months ago b74751b vpc
wip - add vpc, prefix support, need testing
M box.go => box.go +11 -0
@@ 13,6 13,7 @@ type Box struct {
	RAM   datasize `json:"ram"`
	Disk  datasize `json:"disk"`
	Image string   `json:"image"`
	Net   Network  `json:"net"`

	// AllowHTTP will disable any cloud-based firewall on ports 80 and 443.
	// This is useful for reverse-proxy boxes that need to be exposed to


@@ 22,6 23,16 @@ type Box struct {

type BoxName string

type Network struct {
	// VPC describes the IP subnets in which this box should be assigned.
	VPC *VPC `json:"vpc,omitempty"`
}

type VPC struct {
	Network    string
	Subnetwork string
}

// datasize is a representation of data in kilobytes.
type datasize int


M cloud_provider.go => cloud_provider.go +1 -0
@@ 5,6 5,7 @@ import (
	"fmt"
)

// TODO(egtann) add {Get,Create}VPC
type CloudProvider interface {
	// GetAll VMs on the cloud provider.
	GetAll(context.Context) ([]*VM, error)

M cmd/terrafirma/main.go => cmd/terrafirma/main.go +2 -1
@@ 38,6 38,7 @@ func run() error {
		bins       = flag.String("b", "", "bins")
		timeout    = flag.Duration("t", 5*time.Minute, "timeout")
		external   = flag.Bool("x", false, "show external ips (inventory only)")
		prefix     = flag.String("pre", "tf", "prefix")
	)
	flag.Parse()



@@ 77,7 78,7 @@ func run() error {
	token := os.Getenv("TOKEN")
	cloudProvider := google.New(&http.Client{}, project, region, zone,
		token)
	terra := tf.New(cloudProvider, *timeout)
	terra := tf.New(cloudProvider, *prefix, *timeout)

	if cmd == "plan" {
		var services map[tf.BoxName][][]string

M google/google.go => google/google.go +14 -2
@@ 64,6 64,7 @@ type initializeParams struct {
type networkInterface struct {
	Name          string          `json:"name"`
	Network       string          `json:"network"`
	Subnetwork    string          `json:"subnetwork,omitempty"`
	AccessConfigs []*accessConfig `json:"accessConfigs"`

	// NetworkIP is available after creating boxes


@@ 488,6 489,16 @@ func (g *Google) vmToGoogle(v *tf.VM) (*vm, error) {
		v.Tags = append(v.Tags, "http-server")
		v.Tags = append(v.Tags, "https-server")
	}

	// Define the VPC to assign this VM into, if not the default
	var (
		network    = "global/networks/default"
		subnetwork = ""
	)
	if v.Net.VPC != nil {
		network = v.Net.VPC.Network
		subnetwork = v.Net.VPC.Subnetwork
	}
	googleVM := &vm{
		Name:        v.Name,
		MachineType: machineType(mt),


@@ 501,8 512,9 @@ func (g *Google) vmToGoogle(v *tf.VM) (*vm, error) {
			},
		}},
		NetworkInterfaces: []*networkInterface{{
			Network:   "global/networks/default",
			NetworkIP: internalIP,
			Network:    network,
			Subnetwork: subnetwork,
			NetworkIP:  internalIP,
			AccessConfigs: []*accessConfig{{
				Type:  "ONE_TO_ONE_NAT",
				Name:  "External NAT",

M google/google_test.go => google/google_test.go +6 -0
@@ 119,6 119,12 @@ func TestCreateVM(t *testing.T) {
		Mem:  3840,
		Disk: 100,
		Tags: []string{"tag1"},
		Net: tf.Network{
			VPC: &tf.VPC{
				Network:    "net1",
				Subnetwork: "sub1",
			},
		},
		IPs: []*tf.IP{
			{Name: "tf-ip-1", Addr: "internal", Type: tf.IPInternal},
			{Name: "tf-ip-2", Addr: "external", Type: tf.IPExternal},

M google/testdata/instances_get.json => google/testdata/instances_get.json +2 -2
@@ 16,8 16,8 @@
    "canIpForward": false,
    "networkInterfaces": [
        {
            "network": "",
            "subnetwork": "",
            "network": "net1",
            "subnetwork": "sub1",
            "networkIP": "10.0.0.1",
            "name": "",
            "accessConfigs": [

M terrafirma.go => terrafirma.go +9 -8
@@ 10,10 10,9 @@ import (
	"time"
)

const prefix = "tf"

type Terrafirma struct {
	CloudProvider
	prefix  string
	timeout time.Duration
}



@@ 21,16 20,18 @@ type Inventory map[string][]string

func New(
	provider CloudProvider,
	prefix string,
	timeout time.Duration,
) *Terrafirma {
	return &Terrafirma{
		CloudProvider: provider,
		prefix:        prefix,
		timeout:       timeout,
	}
}

func Name(resource, detail string, i int) string {
	return fmt.Sprintf("%s-%s-%s-%d", prefix, resource, detail, i)
func (t *Terrafirma) Name(resource, detail string, i int) string {
	return fmt.Sprintf("%s-%s-%s-%d", t.prefix, resource, detail, i)
}

// vmFromBox initializes a base VM.


@@ 133,7 134,7 @@ func (t *Terrafirma) Plan(
		} else {
			n := rand.Intn(999999-100000) + 100000
			vm.IPs = append(vm.IPs, &IP{
				Name: Name("ip", "internal", n),
				Name: t.Name("ip", "internal", n),
				Type: IPInternal,
			})
		}


@@ 142,7 143,7 @@ func (t *Terrafirma) Plan(
		} else {
			n := rand.Intn(999999-100000) + 100000
			vm.IPs = append(vm.IPs, &IP{
				Name: Name("ip", "external", n),
				Name: t.Name("ip", "external", n),
				Type: IPExternal,
			})
		}


@@ 227,7 228,7 @@ func (t *Terrafirma) plan(
		for i := 0; i < count; i++ {
			n := rand.Intn(999999-100000) + 100000
			p.Create = append(p.Create, &VMTemplate{
				VMName:    Name("vm", string(boxName), n),
				VMName:    t.Name("vm", string(boxName), n),
				BoxName:   boxName,
				Tags:      strings.Split(tag, ","),
				AllowHTTP: allowHTTP,


@@ 358,7 359,7 @@ func (t *Terrafirma) getManagedVMs(ctx context.Context) ([]*VM, error) {
	}
	var vms []*VM
	for _, vm := range tmpVMs {
		if !strings.HasPrefix(vm.Name, prefix+"-") {
		if !strings.HasPrefix(vm.Name, t.prefix+"-") {
			continue
		}
		sort.Strings(vm.Tags)

M vm.go => vm.go +3 -0
@@ 23,6 23,9 @@ type VM struct {
	// IPs assigned to the box.
	IPs []*IP `json:"ips,omitempty"`

	// Net defines network settings such as any VPC.
	Net Network `json:"net"`

	// AllowHTTP will disable any cloud-based firewall on ports 80 and 443.
	// This is useful for reverse-proxy boxes that need to be exposed to
	// the public internet.