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