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

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package cloud
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/cmd"
    10  	"github.com/juju/errors"
    11  	"github.com/juju/gnuflag"
    12  	"gopkg.in/yaml.v2"
    13  
    14  	jujucloud "github.com/juju/juju/cloud"
    15  	jujucmd "github.com/juju/juju/cmd"
    16  	"github.com/juju/juju/cmd/juju/common"
    17  )
    18  
    19  type showCloudCommand struct {
    20  	cmd.CommandBase
    21  	out cmd.Output
    22  
    23  	CloudName string
    24  
    25  	includeConfig bool
    26  }
    27  
    28  var showCloudDoc = `
    29  Provided information includes 'defined' (public, built-in), 'type',
    30  'auth-type', 'regions', 'endpoints', and cloud specific configuration
    31  options.
    32  
    33  If ‘--include-config’ is used, additional configuration (key, type, and
    34  description) specific to the cloud are displayed if available.
    35  
    36  Examples:
    37  
    38      juju show-cloud google
    39      juju show-cloud azure-china --output ~/azure_cloud_details.txt
    40  
    41  See also:
    42      clouds
    43      update-clouds
    44  `
    45  
    46  // NewShowCloudCommand returns a command to list cloud information.
    47  func NewShowCloudCommand() cmd.Command {
    48  	return &showCloudCommand{}
    49  }
    50  
    51  func (c *showCloudCommand) SetFlags(f *gnuflag.FlagSet) {
    52  	c.CommandBase.SetFlags(f)
    53  	// We only support yaml for display purposes.
    54  	c.out.AddFlags(f, "yaml", map[string]cmd.Formatter{
    55  		"yaml": cmd.FormatYaml,
    56  	})
    57  	f.BoolVar(&c.includeConfig, "include-config", false, "Print available config option details specific to the specified cloud")
    58  }
    59  
    60  func (c *showCloudCommand) Init(args []string) error {
    61  	switch len(args) {
    62  	case 1:
    63  		c.CloudName = args[0]
    64  	default:
    65  		return errors.New("no cloud specified")
    66  	}
    67  	return cmd.CheckEmpty(args[1:])
    68  }
    69  
    70  func (c *showCloudCommand) Info() *cmd.Info {
    71  	return jujucmd.Info(&cmd.Info{
    72  		Name:    "show-cloud",
    73  		Args:    "<cloud name>",
    74  		Purpose: "Shows detailed information on a cloud.",
    75  		Doc:     showCloudDoc,
    76  	})
    77  }
    78  
    79  func (c *showCloudCommand) Run(ctxt *cmd.Context) error {
    80  	details, err := GetAllCloudDetails()
    81  	if err != nil {
    82  		return err
    83  	}
    84  	cloud, ok := details[c.CloudName]
    85  	if !ok {
    86  		return errors.NotFoundf("cloud %q", c.CloudName)
    87  	}
    88  	if err = c.out.Write(ctxt, cloud); err != nil {
    89  		return err
    90  	}
    91  	if c.includeConfig {
    92  		config := getCloudConfigDetails(cloud.CloudType)
    93  		if len(config) > 0 {
    94  			fmt.Fprintln(ctxt.Stdout, fmt.Sprintf("\nThe available config options specific to %s clouds are:", cloud.CloudType))
    95  			return c.out.Write(ctxt, config)
    96  		}
    97  	}
    98  	return nil
    99  }
   100  
   101  // RegionDetails holds region details.
   102  type RegionDetails struct {
   103  	Name             string `yaml:"-" json:"-"`
   104  	Endpoint         string `yaml:"endpoint,omitempty" json:"endpoint,omitempty"`
   105  	IdentityEndpoint string `yaml:"identity-endpoint,omitempty" json:"identity-endpoint,omitempty"`
   106  	StorageEndpoint  string `yaml:"storage-endpoint,omitempty" json:"storage-endpoint,omitempty"`
   107  }
   108  
   109  // CloudDetails holds cloud details.
   110  type CloudDetails struct {
   111  	Source           string   `yaml:"defined,omitempty" json:"defined,omitempty"`
   112  	CloudType        string   `yaml:"type" json:"type"`
   113  	CloudDescription string   `yaml:"description" json:"description"`
   114  	AuthTypes        []string `yaml:"auth-types,omitempty,flow" json:"auth-types,omitempty"`
   115  	Endpoint         string   `yaml:"endpoint,omitempty" json:"endpoint,omitempty"`
   116  	IdentityEndpoint string   `yaml:"identity-endpoint,omitempty" json:"identity-endpoint,omitempty"`
   117  	StorageEndpoint  string   `yaml:"storage-endpoint,omitempty" json:"storage-endpoint,omitempty"`
   118  	// Regions is for when we want to print regions in order for yaml output.
   119  	Regions yaml.MapSlice `yaml:"regions,omitempty" json:"-"`
   120  	// Regions map is for json marshalling where format is important but not order.
   121  	RegionsMap    map[string]RegionDetails `yaml:"-" json:"regions,omitempty"`
   122  	Config        map[string]interface{}   `yaml:"config,omitempty" json:"config,omitempty"`
   123  	RegionConfig  jujucloud.RegionConfig   `yaml:"region-config,omitempty" json:"region-config,omitempty"`
   124  	CACredentials []string                 `yaml:"ca-credentials,omitempty" json:"ca-credentials,omitempty"`
   125  }
   126  
   127  func makeCloudDetails(cloud jujucloud.Cloud) *CloudDetails {
   128  	result := &CloudDetails{
   129  		Source:           "public",
   130  		CloudType:        cloud.Type,
   131  		Endpoint:         cloud.Endpoint,
   132  		IdentityEndpoint: cloud.IdentityEndpoint,
   133  		StorageEndpoint:  cloud.StorageEndpoint,
   134  		Config:           cloud.Config,
   135  		RegionConfig:     cloud.RegionConfig,
   136  		CloudDescription: cloud.Description,
   137  		CACredentials:    cloud.CACertificates,
   138  	}
   139  	result.AuthTypes = make([]string, len(cloud.AuthTypes))
   140  	for i, at := range cloud.AuthTypes {
   141  		result.AuthTypes[i] = string(at)
   142  	}
   143  	result.RegionsMap = make(map[string]RegionDetails)
   144  	for _, region := range cloud.Regions {
   145  		r := RegionDetails{Name: region.Name}
   146  		if region.Endpoint != result.Endpoint {
   147  			r.Endpoint = region.Endpoint
   148  		}
   149  		if region.IdentityEndpoint != result.IdentityEndpoint {
   150  			r.IdentityEndpoint = region.IdentityEndpoint
   151  		}
   152  		if region.StorageEndpoint != result.StorageEndpoint {
   153  			r.StorageEndpoint = region.StorageEndpoint
   154  		}
   155  		result.Regions = append(result.Regions, yaml.MapItem{r.Name, r})
   156  		result.RegionsMap[region.Name] = r
   157  	}
   158  	return result
   159  }
   160  
   161  func getCloudConfigDetails(cloudType string) map[string]interface{} {
   162  	// providerSchema has all config options, including their descriptions
   163  	// and types.
   164  	providerSchema, err := common.CloudSchemaByType(cloudType)
   165  	if err != nil {
   166  		// Some providers do not implement the ProviderSchema interface.
   167  		return nil
   168  	}
   169  	specifics := make(map[string]interface{})
   170  	ps, err := common.ProviderConfigSchemaSourceByType(cloudType)
   171  	if err != nil {
   172  		// Some providers do not implement the ConfigSchema interface.
   173  		return nil
   174  	}
   175  	// ps.ConfigSchema() returns the provider specific config option names, but no
   176  	// description etc.
   177  	for attr := range ps.ConfigSchema() {
   178  		if providerSchema[attr].Secret {
   179  			continue
   180  		}
   181  		specifics[attr] = common.PrintConfigSchema{
   182  			Description: providerSchema[attr].Description,
   183  			Type:        fmt.Sprintf("%s", providerSchema[attr].Type),
   184  		}
   185  	}
   186  	return specifics
   187  }
   188  
   189  // GetAllCloudDetails returns a list of all cloud details.
   190  func GetAllCloudDetails() (map[string]*CloudDetails, error) {
   191  	result, err := listCloudDetails()
   192  	if err != nil {
   193  		return nil, errors.Trace(err)
   194  	}
   195  	return result.all(), nil
   196  }