~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/environs/bootstrap/tools.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2012, 2013 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package bootstrap
 
5
 
 
6
import (
 
7
        "fmt"
 
8
 
 
9
        "github.com/juju/errors"
 
10
        "github.com/juju/utils/arch"
 
11
        jujuos "github.com/juju/utils/os"
 
12
        "github.com/juju/utils/series"
 
13
        "github.com/juju/utils/set"
 
14
        "github.com/juju/version"
 
15
 
 
16
        "github.com/juju/juju/constraints"
 
17
        "github.com/juju/juju/environs"
 
18
        envtools "github.com/juju/juju/environs/tools"
 
19
        coretools "github.com/juju/juju/tools"
 
20
        jujuversion "github.com/juju/juju/version"
 
21
)
 
22
 
 
23
var (
 
24
        findTools = envtools.FindTools
 
25
)
 
26
 
 
27
// validateUploadAllowed returns an error if an attempt to upload tools should
 
28
// not be allowed.
 
29
func validateUploadAllowed(env environs.Environ, toolsArch, toolsSeries *string) error {
 
30
        // Now check that the architecture and series for which we are setting up an
 
31
        // environment matches that from which we are bootstrapping.
 
32
        hostArch := arch.HostArch()
 
33
        // We can't build tools for a different architecture if one is specified.
 
34
        if toolsArch != nil && *toolsArch != hostArch {
 
35
                return fmt.Errorf("cannot build tools for %q using a machine running on %q", *toolsArch, hostArch)
 
36
        }
 
37
        hostOS := jujuos.HostOS()
 
38
        if toolsSeries != nil {
 
39
                toolsSeriesOS, err := series.GetOSFromSeries(*toolsSeries)
 
40
                if err != nil {
 
41
                        return errors.Trace(err)
 
42
                }
 
43
                if toolsSeriesOS != hostOS {
 
44
                        return errors.Errorf("cannot build tools for %q using a machine running %q", *toolsSeries, hostOS)
 
45
                }
 
46
        }
 
47
        // If no architecture is specified, ensure the target provider supports instances matching our architecture.
 
48
        validator, err := env.ConstraintsValidator()
 
49
        if err != nil {
 
50
                return errors.Annotate(err,
 
51
                        "no packaged tools available and cannot determine model's supported architectures",
 
52
                )
 
53
        }
 
54
        if _, err := validator.Validate(constraints.Value{Arch: &hostArch}); err != nil {
 
55
                return errors.Errorf(
 
56
                        "model %q of type %s does not support instances running on %q",
 
57
                        env.Config().Name(), env.Config().Type(), hostArch,
 
58
                )
 
59
        }
 
60
        return nil
 
61
}
 
62
 
 
63
// findAvailableTools returns a list of available tools,
 
64
// including tools that may be locally built and then
 
65
// uploaded. Tools that need to be built will have an
 
66
// empty URL.
 
67
func findAvailableTools(
 
68
        env environs.Environ,
 
69
        vers *version.Number,
 
70
        arch, series *string,
 
71
        upload, canBuild bool,
 
72
) (coretools.List, error) {
 
73
        if upload {
 
74
                // We're forcing an upload: ensure we can do so.
 
75
                if !canBuild {
 
76
                        return nil, errors.New("cannot build tools to upload")
 
77
                }
 
78
                if err := validateUploadAllowed(env, arch, series); err != nil {
 
79
                        return nil, err
 
80
                }
 
81
                return locallyBuildableTools(series), nil
 
82
        }
 
83
 
 
84
        // We're not forcing an upload, so look for tools
 
85
        // in the environment's simplestreams search paths
 
86
        // for existing tools.
 
87
 
 
88
        // If the user hasn't asked for a specified tools version, see if
 
89
        // one is configured in the environment.
 
90
        if vers == nil {
 
91
                if agentVersion, ok := env.Config().AgentVersion(); ok {
 
92
                        vers = &agentVersion
 
93
                }
 
94
        }
 
95
        logger.Infof("looking for bootstrap tools: version=%v", vers)
 
96
        toolsList, findToolsErr := findBootstrapTools(env, vers, arch, series)
 
97
        if findToolsErr != nil && !errors.IsNotFound(findToolsErr) {
 
98
                return nil, findToolsErr
 
99
        }
 
100
 
 
101
        preferredStream := envtools.PreferredStream(vers, env.Config().Development(), env.Config().AgentStream())
 
102
        if preferredStream == envtools.ReleasedStream || vers != nil || !canBuild {
 
103
                // We are not running a development build, or agent-version
 
104
                // was specified, or we cannot build any tools; the only tools
 
105
                // available are the ones we've just found.
 
106
                return toolsList, findToolsErr
 
107
        }
 
108
        // The tools located may not include the ones that the
 
109
        // provider requires. We are running a development build,
 
110
        // so augment the list of tools with those that we can build
 
111
        // locally.
 
112
 
 
113
        // Collate the set of arch+series that are externally available
 
114
        // so we can see if we need to build any locally. If we need
 
115
        // to, only then do we validate that we can upload (which
 
116
        // involves a potentially expensive ConstraintsValidator call).
 
117
        archSeries := make(set.Strings)
 
118
        for _, tools := range toolsList {
 
119
                archSeries.Add(tools.Version.Arch + tools.Version.Series)
 
120
        }
 
121
        var localToolsList coretools.List
 
122
        for _, tools := range locallyBuildableTools(series) {
 
123
                if !archSeries.Contains(tools.Version.Arch + tools.Version.Series) {
 
124
                        localToolsList = append(localToolsList, tools)
 
125
                }
 
126
        }
 
127
        if len(localToolsList) == 0 || validateUploadAllowed(env, arch, series) != nil {
 
128
                return toolsList, findToolsErr
 
129
        }
 
130
        return append(toolsList, localToolsList...), nil
 
131
}
 
132
 
 
133
// locallyBuildableTools returns the list of tools that
 
134
// can be built locally, for series of the same OS.
 
135
func locallyBuildableTools(toolsSeries *string) (buildable coretools.List) {
 
136
        for _, ser := range series.SupportedSeries() {
 
137
                if os, err := series.GetOSFromSeries(ser); err != nil || os != jujuos.HostOS() {
 
138
                        continue
 
139
                }
 
140
                if toolsSeries != nil && ser != *toolsSeries {
 
141
                        continue
 
142
                }
 
143
                binary := version.Binary{
 
144
                        Number: jujuversion.Current,
 
145
                        Series: ser,
 
146
                        Arch:   arch.HostArch(),
 
147
                }
 
148
                // Increment the build number so we know it's a development build.
 
149
                binary.Build++
 
150
                buildable = append(buildable, &coretools.Tools{Version: binary})
 
151
        }
 
152
        return buildable
 
153
}
 
154
 
 
155
// findBootstrapTools returns a tools.List containing only those tools with
 
156
// which it would be reasonable to launch an environment's first machine,
 
157
// given the supplied constraints. If a specific agent version is not requested,
 
158
// all tools matching the current major.minor version are chosen.
 
159
func findBootstrapTools(env environs.Environ, vers *version.Number, arch, series *string) (list coretools.List, err error) {
 
160
        // Construct a tools filter.
 
161
        cliVersion := jujuversion.Current
 
162
        var filter coretools.Filter
 
163
        if arch != nil {
 
164
                filter.Arch = *arch
 
165
        }
 
166
        if series != nil {
 
167
                filter.Series = *series
 
168
        }
 
169
        if vers != nil {
 
170
                filter.Number = *vers
 
171
        }
 
172
        stream := envtools.PreferredStream(vers, env.Config().Development(), env.Config().AgentStream())
 
173
        return findTools(env, cliVersion.Major, cliVersion.Minor, stream, filter)
 
174
}