github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/plugins/juju-metadata/validatetoolsmetadata.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "bytes" 8 "fmt" 9 "os" 10 "strings" 11 12 "github.com/juju/cmd" 13 "github.com/juju/errors" 14 "github.com/juju/gnuflag" 15 "github.com/juju/utils/arch" 16 "github.com/juju/version" 17 18 jujucmd "github.com/juju/juju/cmd" 19 "github.com/juju/juju/cmd/modelcmd" 20 "github.com/juju/juju/cmd/output" 21 "github.com/juju/juju/environs" 22 "github.com/juju/juju/environs/simplestreams" 23 "github.com/juju/juju/environs/tools" 24 jujuversion "github.com/juju/juju/version" 25 ) 26 27 func newValidateToolsMetadataCommand() cmd.Command { 28 return modelcmd.WrapController(&validateToolsMetadataCommand{}) 29 } 30 31 // validateToolsMetadataCommand 32 type validateToolsMetadataCommand struct { 33 modelcmd.ControllerCommandBase 34 35 out cmd.Output 36 providerType string 37 metadataDir string 38 stream string 39 series string 40 region string 41 endpoint string 42 exactVersion string 43 partVersion string 44 major int 45 minor int 46 } 47 48 var validateToolsMetadataDoc = ` 49 validate-agents loads simplestreams metadata and validates the contents by 50 looking for agent binaries belonging to the specified series, architecture, for the 51 specified cloud. If version is specified, agent binaries matching the exact specified 52 version are found. It is also possible to just specify the major (and optionally 53 minor) version numbers to search for. 54 55 The cloud specification comes from the current Juju model, as specified in 56 the usual way from either the -m option, or JUJU_MODEL. Series, Region, and 57 Endpoint are the key attributes. 58 59 It is possible to specify a local directory containing agent metadata, 60 in which case cloud attributes like provider type, region etc are optional. 61 62 The key model attributes may be overridden using command arguments, so 63 that the validation may be performed on arbitrary metadata. 64 65 Examples: 66 67 - validate using the current model settings but with series raring 68 69 juju metadata validate-agents -s raring 70 71 - validate using the current model settings but with Juju version 1.11.4 72 73 juju metadata validate-agents -j 1.11.4 74 75 - validate using the current model settings but with Juju major version 2 76 77 juju metadata validate-agents -m 2 78 79 - validate using the current model settings but with Juju major.minor version 2.1 80 81 juju metadata validate-agents -m 2.1 82 83 - validate using the current model settings and list all agent binaries found for any series 84 85 juju metadata validate-agents --series= 86 87 - validate with series raring and using metadata from local directory 88 89 juju metadata validate-agents -s raring -d <some directory> 90 91 - validate for the proposed stream 92 93 juju metadata validate-agents --stream proposed 94 95 A key use case is to validate newly generated metadata prior to deployment to 96 production. In this case, the metadata is placed in a local directory, a cloud 97 provider type is specified (ec2, openstack etc), and the validation is performed 98 for each supported series, version, and arcgitecture. 99 100 Example bash snippet: 101 102 #!/bin/bash 103 104 juju metadata validate-agents -p ec2 -r us-east-1 -s precise --juju-version 1.12.0 -d <some directory> 105 RETVAL=$? 106 [ $RETVAL -eq 0 ] && echo Success 107 [ $RETVAL -ne 0 ] && echo Failure 108 ` 109 110 func (c *validateToolsMetadataCommand) Info() *cmd.Info { 111 return jujucmd.Info(&cmd.Info{ 112 Name: "validate-agents", 113 Purpose: "validate agent metadata and ensure agent binary tarball(s) exist for Juju version(s)", 114 Doc: validateToolsMetadataDoc, 115 Aliases: []string{"validate-tools"}, 116 }) 117 } 118 119 func (c *validateToolsMetadataCommand) SetFlags(f *gnuflag.FlagSet) { 120 c.out.AddFlags(f, "yaml", output.DefaultFormatters) 121 f.StringVar(&c.providerType, "p", "", "the provider type eg ec2, openstack") 122 f.StringVar(&c.metadataDir, "d", "", "directory where metadata files are found") 123 f.StringVar(&c.series, "s", "", "the series for which to validate (overrides env config series)") 124 f.StringVar(&c.series, "series", "", "") 125 f.StringVar(&c.region, "r", "", "the region for which to validate (overrides env config region)") 126 f.StringVar(&c.endpoint, "u", "", "the cloud endpoint URL for which to validate (overrides env config endpoint)") 127 f.StringVar(&c.exactVersion, "j", "current", "the Juju version (use 'current' for current version)") 128 f.StringVar(&c.exactVersion, "juju-version", "", "") 129 f.StringVar(&c.partVersion, "majorminor-version", "", "") 130 f.StringVar(&c.stream, "stream", tools.ReleasedStream, "simplestreams stream for which to generate the metadata") 131 } 132 133 func (c *validateToolsMetadataCommand) Init(args []string) error { 134 if c.providerType != "" { 135 if c.region == "" { 136 return errors.Errorf("region required if provider type is specified") 137 } 138 if c.metadataDir == "" { 139 return errors.Errorf("metadata directory required if provider type is specified") 140 } 141 } 142 if c.exactVersion == "current" { 143 c.exactVersion = jujuversion.Current.String() 144 } 145 if c.partVersion != "" { 146 var err error 147 if c.major, c.minor, err = version.ParseMajorMinor(c.partVersion); err != nil { 148 return err 149 } 150 } 151 return cmd.CheckEmpty(args) 152 } 153 154 func (c *validateToolsMetadataCommand) Run(context *cmd.Context) error { 155 var params *simplestreams.MetadataLookupParams 156 if c.providerType == "" { 157 context.Infof("no provider type specified, using bootstrapped cloud") 158 controllerName, err := c.ControllerName() 159 if err != nil { 160 return errors.Trace(err) 161 } 162 environ, err := prepare(context, controllerName, c.ClientStore()) 163 if err == nil { 164 mdLookup, ok := environ.(simplestreams.MetadataValidator) 165 if !ok { 166 return errors.Errorf("%s provider does not support agent metadata validation", environ.Config().Type()) 167 } 168 params, err = mdLookup.MetadataLookupParams(c.region) 169 if err != nil { 170 return err 171 } 172 params.Sources, err = tools.GetMetadataSources(environ) 173 if err != nil { 174 return err 175 } 176 } else { 177 if c.metadataDir == "" { 178 return err 179 } 180 params = &simplestreams.MetadataLookupParams{} 181 } 182 } else { 183 prov, err := environs.Provider(c.providerType) 184 if err != nil { 185 return err 186 } 187 mdLookup, ok := prov.(simplestreams.MetadataValidator) 188 if !ok { 189 return errors.Errorf("%s provider does not support metadata validation for agents", c.providerType) 190 } 191 params, err = mdLookup.MetadataLookupParams(c.region) 192 if err != nil { 193 return err 194 } 195 } 196 197 if len(params.Architectures) == 0 { 198 params.Architectures = arch.AllSupportedArches 199 } 200 201 if c.series != "" { 202 params.Series = c.series 203 } 204 if c.region != "" { 205 params.Region = c.region 206 } 207 if c.endpoint != "" { 208 params.Endpoint = c.endpoint 209 } 210 if c.metadataDir != "" { 211 if _, err := os.Stat(c.metadataDir); err != nil { 212 return err 213 } 214 toolsURL, err := tools.ToolsURL(c.metadataDir) 215 if err != nil { 216 return err 217 } 218 params.Sources = toolsDataSources(toolsURL) 219 } 220 params.Stream = c.stream 221 222 versions, resolveInfo, err := tools.ValidateToolsMetadata(&tools.ToolsMetadataLookupParams{ 223 MetadataLookupParams: *params, 224 Version: c.exactVersion, 225 Major: c.major, 226 Minor: c.minor, 227 }) 228 if err != nil { 229 if resolveInfo != nil { 230 metadata := map[string]interface{}{ 231 "Resolve Metadata": *resolveInfo, 232 } 233 buff := &bytes.Buffer{} 234 if yamlErr := cmd.FormatYaml(buff, metadata); yamlErr == nil { 235 err = errors.Errorf("%v\n%v", err, buff.String()) 236 } 237 } 238 return err 239 } 240 241 if len(versions) > 0 { 242 metadata := map[string]interface{}{ 243 "Matching Tools Versions": versions, 244 "Resolve Metadata": *resolveInfo, 245 } 246 c.out.Write(context, metadata) 247 } else { 248 var sources []string 249 for _, s := range params.Sources { 250 url, err := s.URL("") 251 if err == nil { 252 sources = append(sources, fmt.Sprintf("- %s (%s)", s.Description(), url)) 253 } 254 } 255 return errors.Errorf("no matching agent binaries using sources:\n%s", strings.Join(sources, "\n")) 256 } 257 return nil 258 }