launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/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  	"fmt"
     8  	"os"
     9  	"strings"
    10  
    11  	"launchpad.net/gnuflag"
    12  
    13  	"launchpad.net/juju-core/cmd"
    14  	"launchpad.net/juju-core/environs"
    15  	"launchpad.net/juju-core/environs/configstore"
    16  	"launchpad.net/juju-core/environs/simplestreams"
    17  	"launchpad.net/juju-core/environs/tools"
    18  	"launchpad.net/juju-core/version"
    19  )
    20  
    21  // ValidateToolsMetadataCommand
    22  type ValidateToolsMetadataCommand struct {
    23  	cmd.EnvCommandBase
    24  	providerType string
    25  	metadataDir  string
    26  	series       string
    27  	region       string
    28  	endpoint     string
    29  	exactVersion string
    30  	partVersion  string
    31  	major        int
    32  	minor        int
    33  }
    34  
    35  var validateToolsMetadataDoc = `
    36  validate-tools loads simplestreams metadata and validates the contents by
    37  looking for tools belonging to the specified series, architecture, for the
    38  specified cloud. If version is specified, tools matching the exact specified
    39  version are found. It is also possible to just specify the major (and optionally
    40  minor) version numbers to search for.
    41  
    42  The cloud specification comes from the current Juju environment, as specified in
    43  the usual way from either ~/.juju/environments.yaml, the -e option, or JUJU_ENV.
    44  Series, Region, and Endpoint are the key attributes.
    45  
    46  It is possible to specify a local directory containing tools metadata, in which
    47  case cloud attributes like provider type, region etc are optional.
    48  
    49  The key environment attributes may be overridden using command arguments, so
    50  that the validation may be peformed on arbitary metadata.
    51  
    52  Examples:
    53  
    54   - validate using the current environment settings but with series raring
    55    
    56    juju metadata validate-tools -s raring
    57  
    58   - validate using the current environment settings but with Juju version 1.11.4
    59    
    60    juju metadata validate-tools -j 1.11.4
    61  
    62   - validate using the current environment settings but with Juju major version 2
    63    
    64    juju metadata validate-tools -m 2
    65  
    66   - validate using the current environment settings but with Juju major.minor version 2.1
    67   
    68    juju metadata validate-tools -m 2.1
    69  
    70   - validate using the current environment settings and list all tools found for any series
    71   
    72    juju metadata validate-tools --series=
    73  
    74   - validate with series raring and using metadata from local directory
    75   
    76    juju metadata validate-images -s raring -d <some directory>
    77  
    78  A key use case is to validate newly generated metadata prior to deployment to
    79  production. In this case, the metadata is placed in a local directory, a cloud
    80  provider type is specified (ec2, openstack etc), and the validation is performed
    81  for each supported series, version, and arcgitecture.
    82  
    83  Example bash snippet:
    84  
    85  #!/bin/bash
    86  
    87  juju metadata validate-tools -p ec2 -r us-east-1 -s precise --juju-version 1.12.0 -d <some directory>
    88  RETVAL=$?
    89  [ $RETVAL -eq 0 ] && echo Success
    90  [ $RETVAL -ne 0 ] && echo Failure
    91  `
    92  
    93  func (c *ValidateToolsMetadataCommand) Info() *cmd.Info {
    94  	return &cmd.Info{
    95  		Name:    "validate-tools",
    96  		Purpose: "validate tools metadata and ensure tools tarball(s) exist for Juju version(s)",
    97  		Doc:     validateToolsMetadataDoc,
    98  	}
    99  }
   100  
   101  func (c *ValidateToolsMetadataCommand) SetFlags(f *gnuflag.FlagSet) {
   102  	c.EnvCommandBase.SetFlags(f)
   103  	f.StringVar(&c.providerType, "p", "", "the provider type eg ec2, openstack")
   104  	f.StringVar(&c.metadataDir, "d", "", "directory where metadata files are found")
   105  	f.StringVar(&c.series, "s", "", "the series for which to validate (overrides env config series)")
   106  	f.StringVar(&c.series, "series", "", "")
   107  	f.StringVar(&c.region, "r", "", "the region for which to validate (overrides env config region)")
   108  	f.StringVar(&c.endpoint, "u", "", "the cloud endpoint URL for which to validate (overrides env config endpoint)")
   109  	f.StringVar(&c.exactVersion, "j", "current", "the Juju version (use 'current' for current version)")
   110  	f.StringVar(&c.exactVersion, "juju-version", "", "")
   111  	f.StringVar(&c.partVersion, "m", "", "the Juju major[.minor] version")
   112  	f.StringVar(&c.partVersion, "majorminor-version", "", "")
   113  }
   114  
   115  func (c *ValidateToolsMetadataCommand) Init(args []string) error {
   116  	if c.providerType != "" {
   117  		if c.region == "" {
   118  			return fmt.Errorf("region required if provider type is specified")
   119  		}
   120  		if c.metadataDir == "" {
   121  			return fmt.Errorf("metadata directory required if provider type is specified")
   122  		}
   123  	}
   124  	if c.exactVersion == "current" {
   125  		c.exactVersion = version.Current.Number.String()
   126  	}
   127  	if c.partVersion != "" {
   128  		var err error
   129  		if c.major, c.minor, err = version.ParseMajorMinor(c.partVersion); err != nil {
   130  			return err
   131  		}
   132  	}
   133  	return c.EnvCommandBase.Init(args)
   134  }
   135  
   136  func (c *ValidateToolsMetadataCommand) Run(context *cmd.Context) error {
   137  	var params *simplestreams.MetadataLookupParams
   138  
   139  	if c.providerType == "" {
   140  		store, err := configstore.Default()
   141  		if err != nil {
   142  			return err
   143  		}
   144  		environ, err := environs.PrepareFromName(c.EnvName, store)
   145  		if err == nil {
   146  			mdLookup, ok := environ.(simplestreams.MetadataValidator)
   147  			if !ok {
   148  				return fmt.Errorf("%s provider does not support tools metadata validation", environ.Config().Type())
   149  			}
   150  			params, err = mdLookup.MetadataLookupParams(c.region)
   151  			if err != nil {
   152  				return err
   153  			}
   154  			params.Sources, err = tools.GetMetadataSources(environ)
   155  			if err != nil {
   156  				return err
   157  			}
   158  		} else {
   159  			if c.metadataDir == "" {
   160  				return err
   161  			}
   162  			params = &simplestreams.MetadataLookupParams{
   163  				Architectures: []string{"amd64", "arm", "i386"},
   164  			}
   165  		}
   166  	} else {
   167  		prov, err := environs.Provider(c.providerType)
   168  		if err != nil {
   169  			return err
   170  		}
   171  		mdLookup, ok := prov.(simplestreams.MetadataValidator)
   172  		if !ok {
   173  			return fmt.Errorf("%s provider does not support tools metadata validation", c.providerType)
   174  		}
   175  		params, err = mdLookup.MetadataLookupParams(c.region)
   176  		if err != nil {
   177  			return err
   178  		}
   179  	}
   180  
   181  	if c.series != "" {
   182  		params.Series = c.series
   183  	}
   184  	if c.region != "" {
   185  		params.Region = c.region
   186  	}
   187  	if c.endpoint != "" {
   188  		params.Endpoint = c.endpoint
   189  	}
   190  	if c.metadataDir != "" {
   191  		if _, err := os.Stat(c.metadataDir); err != nil {
   192  			return err
   193  		}
   194  		params.Sources = []simplestreams.DataSource{simplestreams.NewURLDataSource("file://"+c.metadataDir, simplestreams.VerifySSLHostnames)}
   195  	}
   196  
   197  	versions, err := tools.ValidateToolsMetadata(&tools.ToolsMetadataLookupParams{
   198  		MetadataLookupParams: *params,
   199  		Version:              c.exactVersion,
   200  		Major:                c.major,
   201  		Minor:                c.minor,
   202  	})
   203  	if err != nil {
   204  		return err
   205  	}
   206  
   207  	if len(versions) > 0 {
   208  		fmt.Fprintf(context.Stdout, "matching tools versions:\n%s\n", strings.Join(versions, "\n"))
   209  	} else {
   210  		var urls []string
   211  		for _, s := range params.Sources {
   212  			url, err := s.URL("")
   213  			if err != nil {
   214  				urls = append(urls, url)
   215  			}
   216  		}
   217  		return fmt.Errorf("no matching tools using URLs:\n%s", strings.Join(urls, "\n"))
   218  	}
   219  	return nil
   220  }