github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/agent/format.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package agent
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"strings"
    10  )
    11  
    12  // Current agent config format is defined as follows:
    13  // # format <version>\n   (very first line; <version> is 1.18 or later)
    14  // <config-encoded-as-yaml>
    15  // All of this is saved in a single agent.conf file.
    16  //
    17  // Juju only supports upgrading from single steps, so Juju only needs
    18  // to know about the current format and the format of the previous
    19  // stable release. For convenience, the format name includes the
    20  // version number of the stable release that it will be released with.
    21  // Once this release has happened, the format should be considered
    22  // FIXED, and should no longer be modified. If changes are necessary
    23  // to the format, a new format should be created.
    24  //
    25  // We don't need to create new formats for each release, the version
    26  // number is just a convenience for us to know which stable release
    27  // introduced that format.
    28  
    29  var formats = make(map[string]formatter)
    30  
    31  // The formatter defines the two methods needed by the formatters for
    32  // translating to and from the internal, format agnostic, structure.
    33  type formatter interface {
    34  	version() string
    35  	unmarshal(data []byte) (*configInternal, error)
    36  }
    37  
    38  func registerFormat(format formatter) {
    39  	formats[format.version()] = format
    40  }
    41  
    42  // Once a new format version is introduced:
    43  // - Create a formatter for the new version (including a marshal() method);
    44  // - Call registerFormat in the new format's init() function.
    45  // - Change this to point to the new format;
    46  // - Remove the marshal() method from the old format;
    47  
    48  // currentFormat holds the current agent config version's formatter.
    49  var currentFormat = format_1_18
    50  
    51  // agentConfigFilename is the default file name of used for the agent
    52  // config.
    53  const agentConfigFilename = "agent.conf"
    54  
    55  // formatPrefix is prefix of the first line in an agent config file.
    56  const formatPrefix = "# format "
    57  
    58  func getFormatter(version string) (formatter, error) {
    59  	version = strings.TrimSpace(version)
    60  	format, ok := formats[version]
    61  	if !ok {
    62  		return nil, fmt.Errorf("unknown agent config format %q", version)
    63  	}
    64  	return format, nil
    65  }
    66  
    67  func parseConfigData(data []byte) (formatter, *configInternal, error) {
    68  	i := bytes.IndexByte(data, '\n')
    69  	if i == -1 {
    70  		return nil, nil, fmt.Errorf("invalid agent config format: %s", string(data))
    71  	}
    72  	version, configData := string(data[0:i]), data[i+1:]
    73  	if !strings.HasPrefix(version, formatPrefix) {
    74  		return nil, nil, fmt.Errorf("malformed agent config format %q", version)
    75  	}
    76  	version = strings.TrimPrefix(version, formatPrefix)
    77  	format, err := getFormatter(version)
    78  	if err != nil {
    79  		return nil, nil, err
    80  	}
    81  	config, err := format.unmarshal(configData)
    82  	if err != nil {
    83  		return nil, nil, err
    84  	}
    85  	return format, config, nil
    86  }