github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/application/flags.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package application
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  
    10  	"github.com/juju/errors"
    11  	"gopkg.in/juju/names.v2"
    12  
    13  	"github.com/juju/juju/core/devices"
    14  	"github.com/juju/juju/storage"
    15  )
    16  
    17  type storageFlag struct {
    18  	stores       *map[string]storage.Constraints
    19  	bundleStores *map[string]map[string]storage.Constraints
    20  }
    21  
    22  // Set implements gnuflag.Value.Set.
    23  func (f storageFlag) Set(s string) error {
    24  	fields := strings.SplitN(s, "=", 2)
    25  	if len(fields) < 2 {
    26  		if f.bundleStores != nil {
    27  			return errors.New("expected [<application>:]<store>=<constraints>")
    28  		}
    29  		return errors.New("expected <store>=<constraints>")
    30  	}
    31  	var applicationName, storageName string
    32  	if colon := strings.IndexRune(fields[0], ':'); colon >= 0 {
    33  		if f.bundleStores == nil {
    34  			return errors.New("expected <store>=<constraints>")
    35  		}
    36  		applicationName = fields[0][:colon]
    37  		storageName = fields[0][colon+1:]
    38  	} else {
    39  		storageName = fields[0]
    40  	}
    41  	cons, err := storage.ParseConstraints(fields[1])
    42  	if err != nil {
    43  		return errors.Annotate(err, "cannot parse disk constraints")
    44  	}
    45  	var stores map[string]storage.Constraints
    46  	if applicationName != "" {
    47  		if *f.bundleStores == nil {
    48  			*f.bundleStores = make(map[string]map[string]storage.Constraints)
    49  		}
    50  		stores = (*f.bundleStores)[applicationName]
    51  		if stores == nil {
    52  			stores = make(map[string]storage.Constraints)
    53  			(*f.bundleStores)[applicationName] = stores
    54  		}
    55  	} else {
    56  		if *f.stores == nil {
    57  			*f.stores = make(map[string]storage.Constraints)
    58  		}
    59  		stores = *f.stores
    60  	}
    61  	stores[storageName] = cons
    62  	return nil
    63  }
    64  
    65  // String implements gnuflag.Value.String.
    66  func (f storageFlag) String() string {
    67  	strs := make([]string, 0, len(*f.stores))
    68  	for store, cons := range *f.stores {
    69  		strs = append(strs, fmt.Sprintf("%s=%v", store, cons))
    70  	}
    71  	if f.bundleStores != nil {
    72  		for application, stores := range *f.bundleStores {
    73  			for store, cons := range stores {
    74  				strs = append(strs, fmt.Sprintf("%s:%s=%v", application, store, cons))
    75  			}
    76  		}
    77  	}
    78  	return strings.Join(strs, " ")
    79  }
    80  
    81  type devicesFlag struct {
    82  	devices       *map[string]devices.Constraints
    83  	bundleDevices *map[string]map[string]devices.Constraints
    84  }
    85  
    86  // Set implements gnuflag.Value.Set.
    87  func (f devicesFlag) Set(s string) error {
    88  	fields := strings.SplitN(s, "=", 2)
    89  	if len(fields) < 2 {
    90  		if f.bundleDevices != nil {
    91  			return errors.New("expected [<application>:]<device>=<constraints>")
    92  		}
    93  		return errors.New("expected <device>=<constraints>")
    94  	}
    95  	var applicationName, deviceName string
    96  	if colon := strings.IndexRune(fields[0], ':'); colon >= 0 {
    97  		if f.bundleDevices == nil {
    98  			return errors.New("expected <device>=<constraints>")
    99  		}
   100  		applicationName = fields[0][:colon]
   101  		deviceName = fields[0][colon+1:]
   102  	} else {
   103  		deviceName = fields[0]
   104  	}
   105  	cons, err := devices.ParseConstraints(fields[1])
   106  	if err != nil {
   107  		return errors.Annotate(err, "cannot parse device constraints")
   108  	}
   109  	var devs map[string]devices.Constraints
   110  	if applicationName != "" {
   111  		if *f.bundleDevices == nil {
   112  			*f.bundleDevices = make(map[string]map[string]devices.Constraints)
   113  		}
   114  		devs = (*f.bundleDevices)[applicationName]
   115  		if devs == nil {
   116  			devs = make(map[string]devices.Constraints)
   117  			(*f.bundleDevices)[applicationName] = devs
   118  		}
   119  	} else {
   120  		if *f.devices == nil {
   121  			*f.devices = make(map[string]devices.Constraints)
   122  		}
   123  		devs = *f.devices
   124  	}
   125  	devs[deviceName] = cons
   126  	return nil
   127  }
   128  
   129  // String implements gnuflag.Value.String.
   130  func (f devicesFlag) String() string {
   131  	strs := make([]string, 0, len(*f.devices))
   132  	for device, cons := range *f.devices {
   133  		strs = append(strs, fmt.Sprintf("%s=%v", device, cons))
   134  	}
   135  	if f.bundleDevices != nil {
   136  		for application, devices := range *f.bundleDevices {
   137  			for device, cons := range devices {
   138  				strs = append(strs, fmt.Sprintf("%s:%s=%v", application, device, cons))
   139  			}
   140  		}
   141  	}
   142  	return strings.Join(strs, " ")
   143  }
   144  
   145  type attachStorageFlag struct {
   146  	storageIDs *[]string
   147  }
   148  
   149  // Set implements gnuflag.Value.Set.
   150  func (f attachStorageFlag) Set(s string) error {
   151  	if s == "" {
   152  		return nil
   153  	}
   154  	for _, id := range strings.Split(s, ",") {
   155  		if !names.IsValidStorage(id) {
   156  			return errors.NotValidf("storage ID %q", id)
   157  		}
   158  		*f.storageIDs = append(*f.storageIDs, id)
   159  	}
   160  	return nil
   161  }
   162  
   163  // String implements gnuflag.Value.String.
   164  func (f attachStorageFlag) String() string {
   165  	return strings.Join(*f.storageIDs, ",")
   166  }
   167  
   168  // stringMap is a type that deserializes a CLI string using gnuflag's Value
   169  // semantics.  It expects a name=value pair, and supports multiple copies of the
   170  // flag adding more pairs, though the names must be unique.
   171  type stringMap struct {
   172  	mapping *map[string]string
   173  }
   174  
   175  // Set implements gnuflag.Value's Set method.
   176  func (m stringMap) Set(s string) error {
   177  	if *m.mapping == nil {
   178  		*m.mapping = map[string]string{}
   179  	}
   180  	// make a copy so the following code is less ugly with dereferencing.
   181  	mapping := *m.mapping
   182  
   183  	vals := strings.SplitN(s, "=", 2)
   184  	if len(vals) != 2 {
   185  		return errors.NewNotValid(nil, "badly formatted name value pair: "+s)
   186  	}
   187  	name, value := vals[0], vals[1]
   188  	if _, ok := mapping[name]; ok {
   189  		return errors.Errorf("duplicate name specified: %q", name)
   190  	}
   191  	mapping[name] = value
   192  	return nil
   193  }
   194  
   195  // String implements gnuflag.Value's String method
   196  func (m stringMap) String() string {
   197  	pairs := make([]string, 0, len(*m.mapping))
   198  	for name, value := range *m.mapping {
   199  		pairs = append(pairs, name+"="+value)
   200  	}
   201  	return strings.Join(pairs, ";")
   202  }