1package instance
2
3import (
4	"fmt"
5	"time"
6
7	biblobstore "github.com/cloudfoundry/bosh-cli/blobstore"
8	bicloud "github.com/cloudfoundry/bosh-cli/cloud"
9	bidisk "github.com/cloudfoundry/bosh-cli/deployment/disk"
10	bideplmanifest "github.com/cloudfoundry/bosh-cli/deployment/manifest"
11	bisshtunnel "github.com/cloudfoundry/bosh-cli/deployment/sshtunnel"
12	bivm "github.com/cloudfoundry/bosh-cli/deployment/vm"
13	biinstallmanifest "github.com/cloudfoundry/bosh-cli/installation/manifest"
14	bistemcell "github.com/cloudfoundry/bosh-cli/stemcell"
15	biui "github.com/cloudfoundry/bosh-cli/ui"
16	bosherr "github.com/cloudfoundry/bosh-utils/errors"
17	boshlog "github.com/cloudfoundry/bosh-utils/logger"
18)
19
20type Manager interface {
21	FindCurrent() ([]Instance, error)
22	Create(
23		jobName string,
24		id int,
25		deploymentManifest bideplmanifest.Manifest,
26		cloudStemcell bistemcell.CloudStemcell,
27		registryConfig biinstallmanifest.Registry,
28		eventLoggerStage biui.Stage,
29	) (Instance, []bidisk.Disk, error)
30	DeleteAll(
31		pingTimeout time.Duration,
32		pingDelay time.Duration,
33		skipDrain bool,
34		eventLoggerStage biui.Stage,
35	) error
36}
37
38type manager struct {
39	cloud            bicloud.Cloud
40	vmManager        bivm.Manager
41	blobstore        biblobstore.Blobstore
42	sshTunnelFactory bisshtunnel.Factory
43	instanceFactory  Factory
44	logger           boshlog.Logger
45	logTag           string
46}
47
48func NewManager(
49	cloud bicloud.Cloud,
50	vmManager bivm.Manager,
51	blobstore biblobstore.Blobstore,
52	sshTunnelFactory bisshtunnel.Factory,
53	instanceFactory Factory,
54	logger boshlog.Logger,
55) Manager {
56	return &manager{
57		cloud:            cloud,
58		vmManager:        vmManager,
59		blobstore:        blobstore,
60		sshTunnelFactory: sshTunnelFactory,
61		instanceFactory:  instanceFactory,
62		logger:           logger,
63		logTag:           "vmDeployer",
64	}
65}
66
67func (m *manager) FindCurrent() ([]Instance, error) {
68	instances := []Instance{}
69
70	// Only one current instance will exist (for now)
71	vm, found, err := m.vmManager.FindCurrent()
72	if err != nil {
73		return instances, bosherr.WrapError(err, "Finding currently deployed instances")
74	}
75
76	if found {
77		// TODO: store the name of the job for each instance in the repo, so that we can print it when deleting
78		jobName := "unknown"
79		instanceID := 0
80
81		instance := m.instanceFactory.NewInstance(
82			jobName,
83			instanceID,
84			vm,
85			m.vmManager,
86			m.sshTunnelFactory,
87			m.blobstore,
88			m.logger,
89		)
90		instances = append(instances, instance)
91	}
92
93	return instances, nil
94}
95
96func (m *manager) Create(
97	jobName string,
98	id int,
99	deploymentManifest bideplmanifest.Manifest,
100	cloudStemcell bistemcell.CloudStemcell,
101	registryConfig biinstallmanifest.Registry,
102	eventLoggerStage biui.Stage,
103) (Instance, []bidisk.Disk, error) {
104	var vm bivm.VM
105	stepName := fmt.Sprintf("Creating VM for instance '%s/%d' from stemcell '%s'", jobName, id, cloudStemcell.CID())
106	err := eventLoggerStage.Perform(stepName, func() error {
107		var err error
108		vm, err = m.vmManager.Create(cloudStemcell, deploymentManifest)
109		if err != nil {
110			return bosherr.WrapError(err, "Creating VM")
111		}
112
113		if err = cloudStemcell.PromoteAsCurrent(); err != nil {
114			return bosherr.WrapErrorf(err, "Promoting stemcell as current '%s'", cloudStemcell.CID())
115		}
116
117		return nil
118	})
119	if err != nil {
120		return nil, []bidisk.Disk{}, err
121	}
122
123	instance := m.instanceFactory.NewInstance(jobName, id, vm, m.vmManager, m.sshTunnelFactory, m.blobstore, m.logger)
124
125	if err := instance.WaitUntilReady(registryConfig, eventLoggerStage); err != nil {
126		return instance, []bidisk.Disk{}, bosherr.WrapError(err, "Waiting until instance is ready")
127	}
128
129	disks, err := instance.UpdateDisks(deploymentManifest, eventLoggerStage)
130	if err != nil {
131		return instance, disks, bosherr.WrapError(err, "Updating instance disks")
132	}
133
134	return instance, disks, err
135}
136
137func (m *manager) DeleteAll(
138	pingTimeout time.Duration,
139	pingDelay time.Duration,
140	skipDrain bool,
141	eventLoggerStage biui.Stage,
142) error {
143	instances, err := m.FindCurrent()
144	if err != nil {
145		return err
146	}
147
148	for _, instance := range instances {
149		if err = instance.Delete(pingTimeout, pingDelay, skipDrain, eventLoggerStage); err != nil {
150			return bosherr.WrapErrorf(err, "Deleting existing instance '%s/%d'", instance.JobName(), instance.ID())
151		}
152	}
153	return nil
154}
155