forked from juju/juju
/
facade.go
187 lines (161 loc) · 5.77 KB
/
facade.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
// Package sshclient implements the API endpoint required for Juju
// clients that wish to make SSH connections to Juju managed machines.
package sshclient
import (
"github.com/juju/errors"
"github.com/juju/juju/apiserver/common"
"github.com/juju/juju/apiserver/facade"
"github.com/juju/juju/apiserver/params"
"github.com/juju/juju/network"
"github.com/juju/juju/permission"
)
func init() {
common.RegisterStandardFacade("SSHClient", 1, newFacade)
// Facade version 2 adds AllAddresses() method.
common.RegisterStandardFacade("SSHClient", 2, newFacade)
}
// Facade implements the API required by the sshclient worker.
type Facade struct {
backend Backend
authorizer facade.Authorizer
}
// New returns a new API facade for the sshclient worker.
func New(backend Backend, _ facade.Resources, authorizer facade.Authorizer) (*Facade, error) {
if !authorizer.AuthClient() {
return nil, common.ErrPerm
}
return &Facade{backend: backend, authorizer: authorizer}, nil
}
func (facade *Facade) checkIsModelAdmin() error {
isModelAdmin, err := facade.authorizer.HasPermission(permission.AdminAccess, facade.backend.ModelTag())
if err != nil {
return errors.Trace(err)
}
if !isModelAdmin {
return common.ErrPerm
}
return nil
}
// PublicAddress reports the preferred public network address for one
// or more entities. Machines and units are suppored.
func (facade *Facade) PublicAddress(args params.Entities) (params.SSHAddressResults, error) {
if err := facade.checkIsModelAdmin(); err != nil {
return params.SSHAddressResults{}, errors.Trace(err)
}
getter := func(m SSHMachine) (network.Address, error) { return m.PublicAddress() }
return facade.getAddressPerEntity(args, getter)
}
// PrivateAddress reports the preferred private network address for one or
// more entities. Machines and units are supported.
func (facade *Facade) PrivateAddress(args params.Entities) (params.SSHAddressResults, error) {
if err := facade.checkIsModelAdmin(); err != nil {
return params.SSHAddressResults{}, errors.Trace(err)
}
getter := func(m SSHMachine) (network.Address, error) { return m.PrivateAddress() }
return facade.getAddressPerEntity(args, getter)
}
// AllAddresses reports all addresses known to Juju for each given entity in
// args. Machines and units are supported as entity types. Since the returned
// addresses are gathered from multiple sources, results may include duplicates.
func (facade *Facade) AllAddresses(args params.Entities) (params.SSHAddressesResults, error) {
if err := facade.checkIsModelAdmin(); err != nil {
return params.SSHAddressesResults{}, errors.Trace(err)
}
getter := func(m SSHMachine) ([]network.Address, error) {
devicesAddresses, err := m.AllNetworkAddresses()
if err != nil {
return nil, errors.Trace(err)
}
legacyAddresses := m.Addresses()
return append(devicesAddresses, legacyAddresses...), nil
}
return facade.getAllEntityAddresses(args, getter)
}
func (facade *Facade) getAllEntityAddresses(args params.Entities, getter func(SSHMachine) ([]network.Address, error)) (
params.SSHAddressesResults, error,
) {
out := params.SSHAddressesResults{
Results: make([]params.SSHAddressesResult, len(args.Entities)),
}
for i, entity := range args.Entities {
machine, err := facade.backend.GetMachineForEntity(entity.Tag)
if err != nil {
out.Results[i].Error = common.ServerError(err)
} else {
addresses, err := getter(machine)
if err != nil {
out.Results[i].Error = common.ServerError(err)
continue
}
out.Results[i].Addresses = make([]string, len(addresses))
for j := range addresses {
out.Results[i].Addresses[j] = addresses[j].Value
}
}
}
return out, nil
}
func (facade *Facade) getAddressPerEntity(args params.Entities, addressGetter func(SSHMachine) (network.Address, error)) (
params.SSHAddressResults, error,
) {
out := params.SSHAddressResults{
Results: make([]params.SSHAddressResult, len(args.Entities)),
}
getter := func(m SSHMachine) ([]network.Address, error) {
address, err := addressGetter(m)
if err != nil {
return nil, errors.Trace(err)
}
return []network.Address{address}, nil
}
fullResults, err := facade.getAllEntityAddresses(args, getter)
if err != nil {
return params.SSHAddressResults{}, errors.Trace(err)
}
for i, result := range fullResults.Results {
if result.Error != nil {
out.Results[i].Error = result.Error
} else {
out.Results[i].Address = result.Addresses[0]
}
}
return out, nil
}
// PublicKeys returns the public SSH hosts for one or more
// entities. Machines and units are supported.
func (facade *Facade) PublicKeys(args params.Entities) (params.SSHPublicKeysResults, error) {
if err := facade.checkIsModelAdmin(); err != nil {
return params.SSHPublicKeysResults{}, errors.Trace(err)
}
out := params.SSHPublicKeysResults{
Results: make([]params.SSHPublicKeysResult, len(args.Entities)),
}
for i, entity := range args.Entities {
machine, err := facade.backend.GetMachineForEntity(entity.Tag)
if err != nil {
out.Results[i].Error = common.ServerError(err)
} else {
keys, err := facade.backend.GetSSHHostKeys(machine.MachineTag())
if err != nil {
out.Results[i].Error = common.ServerError(err)
} else {
out.Results[i].PublicKeys = []string(keys)
}
}
}
return out, nil
}
// Proxy returns whether SSH connections should be proxied through the
// controller hosts for the model associated with the API connection.
func (facade *Facade) Proxy() (params.SSHProxyResult, error) {
if err := facade.checkIsModelAdmin(); err != nil {
return params.SSHProxyResult{}, errors.Trace(err)
}
config, err := facade.backend.ModelConfig()
if err != nil {
return params.SSHProxyResult{}, err
}
return params.SSHProxyResult{UseProxy: config.ProxySSH()}, nil
}