github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/api/client/sshclient/facade.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package sshclient
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/names/v5"
     9  
    10  	"github.com/juju/juju/api/base"
    11  	apiservererrors "github.com/juju/juju/apiserver/errors"
    12  	"github.com/juju/juju/cloud"
    13  	"github.com/juju/juju/environs/cloudspec"
    14  	"github.com/juju/juju/rpc/params"
    15  )
    16  
    17  // NewFacade returns a new Facade based on an existing API connection.
    18  func NewFacade(callCloser base.APICallCloser) *Facade {
    19  	clientFacade, caller := base.NewClientFacade(callCloser, "SSHClient")
    20  	return &Facade{
    21  		ClientFacade: clientFacade,
    22  		caller:       caller,
    23  	}
    24  }
    25  
    26  type Facade struct {
    27  	base.ClientFacade
    28  	caller base.FacadeCaller
    29  }
    30  
    31  // PublicAddress returns the public address for the SSH target
    32  // provided. The target may be provided as a machine ID or unit name.
    33  func (facade *Facade) PublicAddress(target string) (string, error) {
    34  	addr, err := facade.addressCall("PublicAddress", target)
    35  	return addr, errors.Trace(err)
    36  }
    37  
    38  // PrivateAddress returns the private address for the SSH target
    39  // provided. The target may be provided as a machine ID or unit name.
    40  func (facade *Facade) PrivateAddress(target string) (string, error) {
    41  	addr, err := facade.addressCall("PrivateAddress", target)
    42  	return addr, errors.Trace(err)
    43  }
    44  
    45  // AllAddresses returns all addresses for the SSH target provided. The target
    46  // may be provided as a machine ID or unit name.
    47  func (facade *Facade) AllAddresses(target string) ([]string, error) {
    48  	entities, err := targetToEntities(target)
    49  	if err != nil {
    50  		return nil, errors.Trace(err)
    51  	}
    52  	var out params.SSHAddressesResults
    53  	err = facade.caller.FacadeCall("AllAddresses", entities, &out)
    54  	if err != nil {
    55  		return nil, errors.Trace(err)
    56  	}
    57  	if len(out.Results) != 1 {
    58  		return nil, countError(len(out.Results))
    59  	}
    60  	if err := out.Results[0].Error; err != nil {
    61  		return nil, errors.Trace(err)
    62  	}
    63  	return out.Results[0].Addresses, nil
    64  }
    65  
    66  func (facade *Facade) addressCall(callName, target string) (string, error) {
    67  	entities, err := targetToEntities(target)
    68  	if err != nil {
    69  		return "", errors.Trace(err)
    70  	}
    71  	var out params.SSHAddressResults
    72  	err = facade.caller.FacadeCall(callName, entities, &out)
    73  	if err != nil {
    74  		return "", errors.Trace(err)
    75  	}
    76  	if len(out.Results) != 1 {
    77  		return "", countError(len(out.Results))
    78  	}
    79  	if err := out.Results[0].Error; err != nil {
    80  		return "", errors.Trace(err)
    81  	}
    82  	return out.Results[0].Address, nil
    83  }
    84  
    85  // PublicKeys returns the SSH public host keys for the SSH target
    86  // provided. The target may be provided as a machine ID or unit name.
    87  func (facade *Facade) PublicKeys(target string) ([]string, error) {
    88  	entities, err := targetToEntities(target)
    89  	if err != nil {
    90  		return nil, errors.Trace(err)
    91  	}
    92  	var out params.SSHPublicKeysResults
    93  	err = facade.caller.FacadeCall("PublicKeys", entities, &out)
    94  	if err != nil {
    95  		return nil, errors.Trace(err)
    96  	}
    97  	if len(out.Results) != 1 {
    98  		return nil, countError(len(out.Results))
    99  	}
   100  	if err := out.Results[0].Error; err != nil {
   101  		return nil, errors.Trace(apiservererrors.RestoreError(err))
   102  	}
   103  	return out.Results[0].PublicKeys, nil
   104  }
   105  
   106  // Proxy returns whether SSH connections should be proxied through the
   107  // controller hosts for the associated model.
   108  func (facade *Facade) Proxy() (bool, error) {
   109  	var out params.SSHProxyResult
   110  	err := facade.caller.FacadeCall("Proxy", nil, &out)
   111  	if err != nil {
   112  		return false, errors.Trace(err)
   113  	}
   114  	return out.UseProxy, nil
   115  }
   116  
   117  func targetToEntities(target string) (params.Entities, error) {
   118  	tag, err := targetToTag(target)
   119  	if err != nil {
   120  		return params.Entities{}, errors.Trace(err)
   121  	}
   122  	return params.Entities{
   123  		Entities: []params.Entity{{Tag: tag.String()}},
   124  	}, nil
   125  }
   126  
   127  func targetToTag(target string) (names.Tag, error) {
   128  	switch {
   129  	case names.IsValidMachine(target):
   130  		return names.NewMachineTag(target), nil
   131  	case names.IsValidUnit(target):
   132  		return names.NewUnitTag(target), nil
   133  	default:
   134  		return nil, errors.NotValidf("target %q", target)
   135  	}
   136  }
   137  
   138  // countError complains about malformed results.
   139  func countError(count int) error {
   140  	return errors.Errorf("expected 1 result, got %d", count)
   141  }
   142  
   143  // ModelCredentialForSSH returns a cloud spec for ssh purpose.
   144  // This facade call is only used for k8s model.
   145  func (facade *Facade) ModelCredentialForSSH() (cloudspec.CloudSpec, error) {
   146  	var result params.CloudSpecResult
   147  
   148  	err := facade.caller.FacadeCall("ModelCredentialForSSH", nil, &result)
   149  	if err != nil {
   150  		return cloudspec.CloudSpec{}, err
   151  	}
   152  	if result.Error != nil {
   153  		err := apiservererrors.RestoreError(result.Error)
   154  		return cloudspec.CloudSpec{}, err
   155  	}
   156  	pSpec := result.Result
   157  	if pSpec == nil {
   158  		return cloudspec.CloudSpec{}, errors.NotValidf("empty value")
   159  	}
   160  	var credential *cloud.Credential
   161  	if pSpec.Credential != nil {
   162  		credentialValue := cloud.NewCredential(
   163  			cloud.AuthType(pSpec.Credential.AuthType),
   164  			pSpec.Credential.Attributes,
   165  		)
   166  		credential = &credentialValue
   167  	}
   168  	spec := cloudspec.CloudSpec{
   169  		Type:              pSpec.Type,
   170  		Name:              pSpec.Name,
   171  		Region:            pSpec.Region,
   172  		Endpoint:          pSpec.Endpoint,
   173  		IdentityEndpoint:  pSpec.IdentityEndpoint,
   174  		StorageEndpoint:   pSpec.StorageEndpoint,
   175  		CACertificates:    pSpec.CACertificates,
   176  		SkipTLSVerify:     pSpec.SkipTLSVerify,
   177  		Credential:        credential,
   178  		IsControllerCloud: pSpec.IsControllerCloud,
   179  	}
   180  	if err := spec.Validate(); err != nil {
   181  		return cloudspec.CloudSpec{}, errors.Annotatef(err, "cannot validate CloudSpec %q", spec.Name)
   182  	}
   183  	return spec, nil
   184  }