github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/cmd/plugins/juju-metadata/signmetadata.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 "io/ioutil" 8 "os" 9 "path/filepath" 10 "strings" 11 12 "github.com/juju/cmd" 13 "github.com/juju/errors" 14 "github.com/juju/gnuflag" 15 "github.com/juju/loggo" 16 17 "github.com/juju/juju/environs/simplestreams" 18 ) 19 20 func newSignMetadataCommand() cmd.Command { 21 return &signMetadataCommand{} 22 } 23 24 var signMetadataDoc = ` 25 sign searches for json files in the specified directory tree and inline signs 26 them using the private key in the specified keyring file. For each .json file, a 27 corresponding .sjson file is procduced. 28 29 The specified keyring file is expected to contain an amored private key. If the key 30 is encrypted, then the specified passphrase is used to decrypt the key. 31 ` 32 33 // signMetadataCommand is used to sign simplestreams metadata json files. 34 type signMetadataCommand struct { 35 cmd.CommandBase 36 dir string 37 keyFile string 38 passphrase string 39 } 40 41 func (c *signMetadataCommand) Info() *cmd.Info { 42 return &cmd.Info{ 43 Name: "sign", 44 Purpose: "sign simplestreams metadata", 45 Doc: signMetadataDoc, 46 } 47 } 48 49 func (c *signMetadataCommand) SetFlags(f *gnuflag.FlagSet) { 50 c.CommandBase.SetFlags(f) 51 f.StringVar(&c.dir, "d", "", "directory in which to look for metadata") 52 f.StringVar(&c.keyFile, "k", "", "file containing the amored private signing key") 53 f.StringVar(&c.passphrase, "p", "", "passphrase used to decrypt the private key") 54 } 55 56 func (c *signMetadataCommand) Init(args []string) error { 57 if c.dir == "" { 58 return errors.Errorf("directory must be specified") 59 } 60 if c.keyFile == "" { 61 return errors.Errorf("keyfile must be specified") 62 } 63 return cmd.CheckEmpty(args) 64 } 65 66 func (c *signMetadataCommand) Run(context *cmd.Context) error { 67 writer := loggo.NewMinimumLevelWriter( 68 cmd.NewCommandLogWriter("juju.plugins.metadata", context.Stdout, context.Stderr), 69 loggo.INFO) 70 loggo.RegisterWriter("signmetadata", writer) 71 defer loggo.RemoveWriter("signmetadata") 72 keyData, err := ioutil.ReadFile(c.keyFile) 73 if err != nil { 74 return err 75 } 76 dir := context.AbsPath(c.dir) 77 return process(dir, string(keyData), c.passphrase) 78 } 79 80 func process(dir, key, passphrase string) error { 81 logger.Debugf("processing directory %q", dir) 82 // Do any json files in dir 83 filenames, err := filepath.Glob(filepath.Join(dir, "*"+simplestreams.UnsignedSuffix)) 84 if len(filenames) > 0 { 85 logger.Infof("signing %d file(s) in %q", len(filenames), dir) 86 } 87 for _, filename := range filenames { 88 logger.Infof("signing file %q", filename) 89 f, err := os.Open(filename) 90 if err != nil { 91 return errors.Errorf("opening file %q: %v", filename, err) 92 } 93 encoded, err := simplestreams.Encode(f, key, passphrase) 94 if err != nil { 95 return errors.Errorf("encoding file %q: %v", filename, err) 96 } 97 signedFilename := strings.Replace(filename, simplestreams.UnsignedSuffix, simplestreams.SignedSuffix, -1) 98 if err = ioutil.WriteFile(signedFilename, encoded, 0644); err != nil { 99 return errors.Errorf("writing signed file %q: %v", signedFilename, err) 100 } 101 } 102 // Now process any directories in dir. 103 files, err := ioutil.ReadDir(dir) 104 if err != nil { 105 return err 106 } 107 for _, f := range files { 108 if f.IsDir() { 109 if err = process(filepath.Join(dir, f.Name()), key, passphrase); err != nil { 110 return err 111 } 112 } 113 } 114 return nil 115 }