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