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