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