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  }