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