github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/cmd/plugins/juju-metadata/validateimagemetadata.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 "fmt" 8 "os" 9 "path/filepath" 10 "strings" 11 12 "github.com/juju/cmd" 13 "github.com/juju/utils" 14 "launchpad.net/gnuflag" 15 16 "github.com/juju/juju/environs" 17 "github.com/juju/juju/environs/config" 18 "github.com/juju/juju/environs/configstore" 19 "github.com/juju/juju/environs/imagemetadata" 20 "github.com/juju/juju/environs/simplestreams" 21 ) 22 23 // ValidateImageMetadataCommand 24 type ValidateImageMetadataCommand struct { 25 ImageMetadataCommandBase 26 out cmd.Output 27 providerType string 28 metadataDir string 29 series string 30 region string 31 endpoint string 32 stream string 33 } 34 35 var validateImagesMetadataDoc = ` 36 validate-images loads simplestreams metadata and validates the contents by 37 looking for images belonging to the specified cloud. 38 39 The cloud specification comes from the current Juju environment, as specified in 40 the usual way from either ~/.juju/environments.yaml, the -e option, or JUJU_ENV. 41 Series, Region, and Endpoint are the key attributes. 42 43 The key environment attributes may be overridden using command arguments, so 44 that the validation may be peformed on arbitary metadata. 45 46 Examples: 47 48 - validate using the current environment settings but with series raring 49 50 juju metadata validate-images -s raring 51 52 - validate using the current environment settings but with series raring and 53 using metadata from local directory (the directory is expected to have an 54 "images" subdirectory containing the metadata, and corresponds to the parameter 55 passed to the image metadata generatation command). 56 57 juju metadata validate-images -s raring -d <some directory> 58 59 A key use case is to validate newly generated metadata prior to deployment to 60 production. In this case, the metadata is placed in a local directory, a cloud 61 provider type is specified (ec2, openstack etc), and the validation is performed 62 for each supported region and series. 63 64 Example bash snippet: 65 66 #!/bin/bash 67 68 juju metadata validate-images -p ec2 -r us-east-1 -s precise -d <some directory> 69 RETVAL=$? 70 [ $RETVAL -eq 0 ] && echo Success 71 [ $RETVAL -ne 0 ] && echo Failure 72 ` 73 74 func (c *ValidateImageMetadataCommand) Info() *cmd.Info { 75 return &cmd.Info{ 76 Name: "validate-images", 77 Purpose: "validate image metadata and ensure image(s) exist for an environment", 78 Doc: validateImagesMetadataDoc, 79 } 80 } 81 82 func (c *ValidateImageMetadataCommand) SetFlags(f *gnuflag.FlagSet) { 83 c.out.AddFlags(f, "smart", cmd.DefaultFormatters) 84 f.StringVar(&c.providerType, "p", "", "the provider type eg ec2, openstack") 85 f.StringVar(&c.metadataDir, "d", "", "directory where metadata files are found") 86 f.StringVar(&c.series, "s", "", "the series for which to validate (overrides env config series)") 87 f.StringVar(&c.region, "r", "", "the region for which to validate (overrides env config region)") 88 f.StringVar(&c.endpoint, "u", "", "the cloud endpoint URL for which to validate (overrides env config endpoint)") 89 f.StringVar(&c.stream, "m", "", "the images stream (defaults to released)") 90 } 91 92 func (c *ValidateImageMetadataCommand) Init(args []string) error { 93 if c.providerType != "" { 94 if c.series == "" { 95 return fmt.Errorf("series required if provider type is specified") 96 } 97 if c.region == "" { 98 return fmt.Errorf("region required if provider type is specified") 99 } 100 if c.metadataDir == "" { 101 return fmt.Errorf("metadata directory required if provider type is specified") 102 } 103 } 104 return cmd.CheckEmpty(args) 105 } 106 107 var _ environs.ConfigGetter = (*overrideEnvStream)(nil) 108 109 // overrideEnvStream implements environs.ConfigGetter and 110 // ensures that the environs.Config returned by Config() 111 // has the specified stream. 112 type overrideEnvStream struct { 113 environs.Environ 114 stream string 115 } 116 117 func (oes *overrideEnvStream) Config() *config.Config { 118 cfg := oes.Environ.Config() 119 // If no stream specified, just use default from environ. 120 if oes.stream == "" { 121 return cfg 122 } 123 newCfg, err := cfg.Apply(map[string]interface{}{"image-stream": oes.stream}) 124 if err != nil { 125 // This should never happen. 126 panic(fmt.Errorf("unexpected error making override config: %v", err)) 127 } 128 return newCfg 129 } 130 131 func (c *ValidateImageMetadataCommand) Run(context *cmd.Context) error { 132 var params *simplestreams.MetadataLookupParams 133 134 if c.providerType == "" { 135 store, err := configstore.Default() 136 if err != nil { 137 return err 138 } 139 environ, err := c.prepare(context, store) 140 if err != nil { 141 return err 142 } 143 mdLookup, ok := environ.(simplestreams.MetadataValidator) 144 if !ok { 145 return fmt.Errorf("%s provider does not support image metadata validation", environ.Config().Type()) 146 } 147 params, err = mdLookup.MetadataLookupParams(c.region) 148 if err != nil { 149 return err 150 } 151 oes := &overrideEnvStream{environ, c.stream} 152 params.Sources, err = environs.ImageMetadataSources(oes) 153 if err != nil { 154 return err 155 } 156 } else { 157 prov, err := environs.Provider(c.providerType) 158 if err != nil { 159 return err 160 } 161 mdLookup, ok := prov.(simplestreams.MetadataValidator) 162 if !ok { 163 return fmt.Errorf("%s provider does not support image metadata validation", c.providerType) 164 } 165 params, err = mdLookup.MetadataLookupParams(c.region) 166 if err != nil { 167 return err 168 } 169 } 170 171 if c.series != "" { 172 params.Series = c.series 173 } 174 if c.region != "" { 175 params.Region = c.region 176 } 177 if c.endpoint != "" { 178 params.Endpoint = c.endpoint 179 } 180 if c.metadataDir != "" { 181 dir := filepath.Join(c.metadataDir, "images") 182 if _, err := os.Stat(dir); err != nil { 183 return err 184 } 185 params.Sources = []simplestreams.DataSource{ 186 simplestreams.NewURLDataSource( 187 "local metadata directory", "file://"+dir, utils.VerifySSLHostnames), 188 } 189 } 190 params.Stream = c.stream 191 192 image_ids, resolveInfo, err := imagemetadata.ValidateImageMetadata(params) 193 if err != nil { 194 if resolveInfo != nil { 195 metadata := map[string]interface{}{ 196 "Resolve Metadata": *resolveInfo, 197 } 198 if metadataYaml, yamlErr := cmd.FormatYaml(metadata); yamlErr == nil { 199 err = fmt.Errorf("%v\n%v", err, string(metadataYaml)) 200 } 201 } 202 return err 203 } 204 if len(image_ids) > 0 { 205 metadata := map[string]interface{}{ 206 "ImageIds": image_ids, 207 "Region": params.Region, 208 "Resolve Metadata": *resolveInfo, 209 } 210 c.out.Write(context, metadata) 211 } else { 212 var sources []string 213 for _, s := range params.Sources { 214 url, err := s.URL("") 215 if err == nil { 216 sources = append(sources, fmt.Sprintf("- %s (%s)", s.Description(), url)) 217 } 218 } 219 return fmt.Errorf( 220 "no matching image ids for region %s using sources:\n%s", 221 params.Region, strings.Join(sources, "\n")) 222 } 223 return nil 224 }