forked from juju/juju
-
Notifications
You must be signed in to change notification settings - Fork 0
/
environ_instance.go
148 lines (133 loc) · 4.93 KB
/
environ_instance.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package lxd
import (
"github.com/juju/errors"
"github.com/juju/version/v2"
"github.com/juju/juju/container/lxd"
"github.com/juju/juju/core/instance"
"github.com/juju/juju/environs"
"github.com/juju/juju/environs/context"
"github.com/juju/juju/environs/instances"
"github.com/juju/juju/environs/tags"
"github.com/juju/juju/provider/common"
)
// Instances returns the available instances in the environment that
// match the provided instance IDs. For IDs that did not match any
// instances, the result at the corresponding index will be nil. In that
// case the error will be environs.ErrPartialInstances (or
// ErrNoInstances if none of the IDs match an instance).
func (env *environ) Instances(ctx context.ProviderCallContext, ids []instance.Id) ([]instances.Instance, error) {
if len(ids) == 0 {
return nil, environs.ErrNoInstances
}
all, err := env.allInstances()
if err != nil {
// We don't return the error since we need to pack one instance
// for each ID into the result. If there is a problem then we
// will return either ErrPartialInstances or ErrNoInstances.
// TODO(ericsnow) Skip returning here only for certain errors?
logger.Errorf("failed to get instances from LXD: %v", err)
common.HandleCredentialError(IsAuthorisationFailure, err, ctx)
err = errors.Trace(err)
}
// Build the result, matching the provided instance IDs.
numFound := 0 // This will never be greater than len(ids).
results := make([]instances.Instance, len(ids))
for i, id := range ids {
inst := findInst(id, all)
if inst != nil {
numFound++
}
results[i] = inst
}
if numFound == 0 {
if err == nil {
err = environs.ErrNoInstances
}
} else if numFound != len(ids) {
err = environs.ErrPartialInstances
}
return results, err
}
func findInst(id instance.Id, instances []*environInstance) instances.Instance {
for _, inst := range instances {
if id == inst.Id() {
return inst
}
}
return nil
}
// instances returns a list of all "alive" instances in the environment.
// We match machine names to the pattern "juju-<model-UUID>-machine-*"
// to ensure that only machines for the environment are returned. This
// is necessary to isolate multiple models within the same LXD.
func (env *environ) allInstances() ([]*environInstance, error) {
prefix := env.namespace.Prefix()
insts, err := env.prefixedInstances(prefix)
return insts, errors.Trace(err)
}
// prefixedInstances returns instances with the specified prefix.
func (env *environ) prefixedInstances(prefix string) ([]*environInstance, error) {
containers, err := env.server().AliveContainers(prefix)
err = errors.Trace(err)
// Turn lxd.Container values into *environInstance values,
// whether or not we got an error.
var results []*environInstance
for _, c := range containers {
c := c
inst := newInstance(&c, env)
results = append(results, inst)
}
return results, err
}
// ControllerInstances returns the IDs of the instances corresponding
// to juju controllers.
func (env *environ) ControllerInstances(ctx context.ProviderCallContext, controllerUUID string) ([]instance.Id, error) {
containers, err := env.server().AliveContainers("juju-")
if err != nil {
common.HandleCredentialError(IsAuthorisationFailure, err, ctx)
return nil, errors.Trace(err)
}
var results []instance.Id
for _, c := range containers {
if c.Metadata(tags.JujuController) != controllerUUID {
continue
}
if c.Metadata(tags.JujuIsController) == "true" {
results = append(results, instance.Id(c.Name))
}
}
if len(results) == 0 {
return nil, environs.ErrNotBootstrapped
}
return results, nil
}
// AdoptResources updates the controller tags on all instances to have the
// new controller id. It's part of the Environ interface.
func (env *environ) AdoptResources(ctx context.ProviderCallContext, controllerUUID string, fromVersion version.Number) error {
instances, err := env.AllInstances(ctx)
if err != nil {
common.HandleCredentialError(IsAuthorisationFailure, err, ctx)
return errors.Annotate(err, "all instances")
}
var failed []instance.Id
qualifiedKey := lxd.UserNamespacePrefix + tags.JujuController
for _, instance := range instances {
id := instance.Id()
// TODO (manadart 2018-06-27) This is a smell.
// Everywhere else, we update the container config on a container and then call WriteContainer.
// If we added a method directly to environInstance to do this, we wouldn't need this
// implementation of UpdateContainerConfig at all, and the container representation we are
// holding would be consistent with that on the server.
err := env.server().UpdateContainerConfig(string(id), map[string]string{qualifiedKey: controllerUUID})
if err != nil {
logger.Errorf("error setting controller uuid tag for %q: %v", id, err)
failed = append(failed, id)
}
}
if len(failed) != 0 {
return errors.Errorf("failed to update controller for some instances: %v", failed)
}
return nil
}