/
cloud.go
179 lines (163 loc) · 5.09 KB
/
cloud.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package common
import (
"bytes"
"strings"
"github.com/juju/cmd/v3"
"github.com/juju/errors"
"github.com/juju/loggo"
"gopkg.in/juju/environschema.v1"
jujucloud "github.com/juju/juju/cloud"
"github.com/juju/juju/environs"
"github.com/juju/juju/environs/config"
)
var logger = loggo.GetLogger("juju.cmd.juju.common")
type chooseCloudRegionError struct {
error
}
// IsChooseCloudRegionError reports whether or not the given
// error was returned from ChooseCloudRegion.
func IsChooseCloudRegionError(err error) bool {
_, ok := errors.Cause(err).(chooseCloudRegionError)
return ok
}
// CloudOrProvider finds and returns cloud or provider.
func CloudOrProvider(cloudName string, cloudByNameFunc func(string) (*jujucloud.Cloud, error)) (cloud *jujucloud.Cloud, err error) {
if cloud, err = cloudByNameFunc(cloudName); err != nil {
if !errors.IsNotFound(err) {
return nil, err
}
builtInClouds, err := BuiltInClouds()
if err != nil {
return nil, errors.Trace(err)
}
if builtIn, ok := builtInClouds[cloudName]; !ok {
return nil, errors.NotValidf("cloud %v", cloudName)
} else {
cloud = &builtIn
}
}
return cloud, nil
}
// ChooseCloudRegion returns the cloud.Region to use, based on the specified
// region name. If no region name is specified, and there is at least one
// region, we use the first region in the list. If there are no regions, then
// we return a region with no name, having the same endpoints as the cloud.
func ChooseCloudRegion(cloud jujucloud.Cloud, regionName string) (jujucloud.Region, error) {
if regionName != "" {
region, err := jujucloud.RegionByName(cloud.Regions, regionName)
if err != nil {
return jujucloud.Region{}, errors.Trace(chooseCloudRegionError{err})
}
return *region, nil
}
if len(cloud.Regions) > 0 {
// No region was specified, use the first region in the list.
return cloud.Regions[0], nil
}
return jujucloud.Region{
"", // no region name
cloud.Endpoint,
cloud.IdentityEndpoint,
cloud.StorageEndpoint,
}, nil
}
// BuiltInClouds returns cloud information for those
// providers which are built in to Juju.
func BuiltInClouds() (map[string]jujucloud.Cloud, error) {
allClouds := make(map[string]jujucloud.Cloud)
for _, providerType := range environs.RegisteredProviders() {
p, err := environs.Provider(providerType)
if err != nil {
return nil, errors.Trace(err)
}
detector, ok := p.(environs.CloudDetector)
if !ok {
continue
}
clouds, err := detector.DetectClouds()
if err != nil {
return nil, errors.Annotatef(
err, "detecting clouds for provider %q",
providerType,
)
}
for _, cloud := range clouds {
allClouds[cloud.Name] = cloud
}
}
return allClouds, nil
}
// CloudByName returns a cloud for given name
// regardless of whether it's public, private or builtin cloud.
// Not to be confused with cloud.CloudByName which does not cater
// for built-in clouds like localhost.
func CloudByName(cloudName string) (*jujucloud.Cloud, error) {
cloud, err := jujucloud.CloudByName(cloudName)
if err != nil {
if errors.IsNotFound(err) {
// Check built in clouds like localhost (lxd).
builtinClouds, err := BuiltInClouds()
if err != nil {
return nil, errors.Trace(err)
}
aCloud, found := builtinClouds[cloudName]
if !found {
return nil, errors.NotFoundf("cloud %s", cloudName)
}
return &aCloud, nil
}
return nil, errors.Trace(err)
}
return cloud, nil
}
// CloudSchemaByType returns the Schema for a given cloud type.
// If the ProviderSchema is not implemented for the given cloud
// type, a NotFound error is returned.
func CloudSchemaByType(cloudType string) (environschema.Fields, error) {
provider, err := environs.Provider(cloudType)
if err != nil {
return nil, err
}
ps, ok := provider.(environs.ProviderSchema)
if !ok {
return nil, errors.NotImplementedf("environs.ProviderSchema")
}
providerSchema := ps.Schema()
if providerSchema == nil {
return nil, errors.New("Failed to retrieve Provider Schema")
}
return providerSchema, nil
}
// ProviderConfigSchemaSourceByType returns a config.ConfigSchemaSource
// for the environ provider, found for the given cloud type, or an error.
func ProviderConfigSchemaSourceByType(cloudType string) (config.ConfigSchemaSource, error) {
provider, err := environs.Provider(cloudType)
if err != nil {
return nil, err
}
if cs, ok := provider.(config.ConfigSchemaSource); ok {
return cs, nil
}
return nil, errors.NotImplementedf("config.ConfigSource")
}
// PrintConfigSchema is used to print model configuration schema.
type PrintConfigSchema struct {
Type string `yaml:"type,omitempty" json:"type,omitempty"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
}
func FormatConfigSchema(values interface{}) (string, error) {
out := &bytes.Buffer{}
err := cmd.FormatSmart(out, values)
if err != nil {
return "", err
}
output := out.String()
// Indent every line by 4 spaces
var indented string
for _, line := range strings.Split(output, "\n") {
indented += " " + line + "\n"
}
return indented, nil
}