github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/cmd/plugins/juju-metadata/toolsmetadata.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  
     9  	"github.com/juju/cmd"
    10  	"github.com/juju/loggo"
    11  	"github.com/juju/utils"
    12  	"launchpad.net/gnuflag"
    13  
    14  	"github.com/juju/juju/cmd/envcmd"
    15  	"github.com/juju/juju/environs/filestorage"
    16  	"github.com/juju/juju/environs/simplestreams"
    17  	"github.com/juju/juju/environs/storage"
    18  	envtools "github.com/juju/juju/environs/tools"
    19  	"github.com/juju/juju/juju/osenv"
    20  	coretools "github.com/juju/juju/tools"
    21  	"github.com/juju/juju/version"
    22  )
    23  
    24  // ToolsMetadataCommand is used to generate simplestreams metadata for juju tools.
    25  type ToolsMetadataCommand struct {
    26  	envcmd.EnvCommandBase
    27  	fetch       bool
    28  	metadataDir string
    29  	stream      string
    30  	clean       bool
    31  	public      bool
    32  }
    33  
    34  var toolsMetadataDoc = `
    35  generate-tools creates simplestreams tools metadata.
    36  
    37  This command works by scanning a directory for tools tarballs from which to generate
    38  simplestreams tools metadata. The working directory is specified using the -d argument
    39  (defaults to ~/.juju). The working directory is expected to contain a named subdirectory
    40  containing tools tarballs, and is where the resulting metadata is written.
    41  
    42  The stream for which metadata is generated is specified using the --stream parameter
    43  (default is "released"). Metadata can be generated for any supported stream - released,
    44  proposed, testing, devel.
    45  
    46  Tools tarballs can are located in either a sub directory called "releases" (legacy),
    47  or a directory named after the stream. By default, if no --stream argument is provided,
    48  metadata for tools in the "released" stream is generated by scanning for tool tarballs
    49  in the "releases" directory. By specifying a stream explcitly, tools tarballs are
    50  expected to be located in a directory named after the stream.
    51  
    52  Newly generated metadata will be merged with any exisitng metadata that is already there.
    53  To first remove metadata for the specified stream before generating new metadata,
    54  use the --clean option.
    55  
    56  Examples:
    57  
    58    - generate metadata for "released" tools, looking in the "releases" directory:
    59  
    60     juju metadata generate-tools -d <workingdir>
    61  
    62    - generate metadata for "released" tools, looking in the "released" directory:
    63  
    64     juju metadata generate-tools -d <workingdir> --stream released
    65  
    66    - generate metadata for "proposed" tools, looking in the "proposed" directory:
    67  
    68     juju metadata generate-tools -d <workingdir> --stream proposed
    69  
    70    - generate metadata for "proposed" tools, first removing existing "proposed" metadata:
    71  
    72     juju metadata generate-tools -d <workingdir> --stream proposed --clean
    73  
    74  `
    75  
    76  func (c *ToolsMetadataCommand) Info() *cmd.Info {
    77  	return &cmd.Info{
    78  		Name:    "generate-tools",
    79  		Purpose: "generate simplestreams tools metadata",
    80  		Doc:     toolsMetadataDoc,
    81  	}
    82  }
    83  
    84  func (c *ToolsMetadataCommand) SetFlags(f *gnuflag.FlagSet) {
    85  	f.StringVar(&c.metadataDir, "d", "", "local directory in which to store metadata")
    86  	// If no stream is specified, we'll generate metadata for the legacy tools location.
    87  	f.StringVar(&c.stream, "stream", "", "simplestreams stream for which to generate the metadata")
    88  	f.BoolVar(&c.clean, "clean", false, "remove any existing metadata for the specified stream before generating new metadata")
    89  	f.BoolVar(&c.public, "public", false, "tools are for a public cloud, so generate mirrors information")
    90  }
    91  
    92  func (c *ToolsMetadataCommand) Run(context *cmd.Context) error {
    93  	loggo.RegisterWriter("toolsmetadata", cmd.NewCommandLogWriter("juju.environs.tools", context.Stdout, context.Stderr), loggo.INFO)
    94  	defer loggo.RemoveWriter("toolsmetadata")
    95  	if c.metadataDir == "" {
    96  		c.metadataDir = osenv.JujuHome()
    97  	} else {
    98  		c.metadataDir = context.AbsPath(c.metadataDir)
    99  	}
   100  
   101  	sourceStorage, err := filestorage.NewFileStorageReader(c.metadataDir)
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	// We now store the tools in a directory named after their stream, but the
   107  	// legacy behaviour is to store all tools in a single "releases" directory.
   108  	toolsDir := c.stream
   109  	if c.stream == "" {
   110  		fmt.Fprintf(context.Stdout, "No stream specified, defaulting to released tools in the releases directory.\n")
   111  		c.stream = envtools.ReleasedStream
   112  		toolsDir = envtools.LegacyReleaseDirectory
   113  	}
   114  	fmt.Fprintf(context.Stdout, "Finding tools in %s for stream %s.\n", c.metadataDir, c.stream)
   115  	const minorVersion = -1
   116  	toolsList, err := envtools.ReadList(sourceStorage, toolsDir, version.Current.Major, minorVersion)
   117  	if err == envtools.ErrNoTools {
   118  		var source string
   119  		source, err = envtools.ToolsURL(envtools.DefaultBaseURL)
   120  		if err != nil {
   121  			return err
   122  		}
   123  		sourceDataSource := simplestreams.NewURLDataSource("local source", source, utils.VerifySSLHostnames)
   124  		toolsList, err = envtools.FindToolsForCloud(
   125  			[]simplestreams.DataSource{sourceDataSource}, simplestreams.CloudSpec{}, c.stream,
   126  			version.Current.Major, minorVersion, coretools.Filter{})
   127  	}
   128  	if err != nil {
   129  		return err
   130  	}
   131  
   132  	targetStorage, err := filestorage.NewFileStorageWriter(c.metadataDir)
   133  	if err != nil {
   134  		return err
   135  	}
   136  	writeMirrors := envtools.DoNotWriteMirrors
   137  	if c.public {
   138  		writeMirrors = envtools.WriteMirrors
   139  	}
   140  	return mergeAndWriteMetadata(targetStorage, toolsDir, c.stream, c.clean, toolsList, writeMirrors)
   141  }
   142  
   143  // This is essentially the same as tools.MergeAndWriteMetadata, but also
   144  // resolves metadata for existing tools by fetching them and computing
   145  // size/sha256 locally.
   146  func mergeAndWriteMetadata(
   147  	stor storage.Storage, toolsDir, stream string, clean bool, toolsList coretools.List, writeMirrors envtools.ShouldWriteMirrors,
   148  ) error {
   149  	existing, err := envtools.ReadAllMetadata(stor)
   150  	if err != nil {
   151  		return err
   152  	}
   153  	if clean {
   154  		delete(existing, stream)
   155  	}
   156  	metadata := envtools.MetadataFromTools(toolsList, toolsDir)
   157  	var mergedMetadata []*envtools.ToolsMetadata
   158  	if mergedMetadata, err = envtools.MergeMetadata(metadata, existing[stream]); err != nil {
   159  		return err
   160  	}
   161  	if err = envtools.ResolveMetadata(stor, toolsDir, mergedMetadata); err != nil {
   162  		return err
   163  	}
   164  	existing[stream] = mergedMetadata
   165  	return envtools.WriteMetadata(stor, existing, []string{stream}, writeMirrors)
   166  }