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