Skip to content

Commit fdc96b3

Browse files
author
Joshua Reed
committed
Most way to parsing secret config.
1 parent 51ace7a commit fdc96b3

File tree

5 files changed

+118
-52
lines changed

5 files changed

+118
-52
lines changed

controllers/cloudstackfailuredomain_controller.go

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package controllers
1919
import (
2020
"context"
2121

22-
2322
"github.com/pkg/errors"
2423
corev1 "k8s.io/api/core/v1"
2524
ctrl "sigs.k8s.io/controller-runtime"
@@ -73,38 +72,36 @@ func (r *CloudStackFailureDomainReconciliationRunner) Reconcile() (retRes ctrl.R
7372
if err := r.K8sClient.Get(r.RequestCtx, key, endpointCredentials); err != nil {
7473
return ctrl.Result{}, errors.Wrapf(err, "getting ACSEndpoint secret with ref: %v", ref)
7574
}
76-
// Prevent premature deletion.
77-
controllerutil.AddFinalizer(r.ReconciliationSubject, infrav1.)
78-
79-
// Start by purely data fetching information about the zone and specified network.
80-
if err := r.CSUser.ResolveZone(r.ReconciliationSubject); err != nil {
81-
return ctrl.Result{}, errors.Wrap(err, "resolving CloudStack zone information")
82-
}
83-
if err := r.CSUser.ResolveNetworkForZone(r.ReconciliationSubject); err != nil &&
84-
!csCtrlrUtils.ContainsNoMatchSubstring(err) {
85-
return ctrl.Result{}, errors.Wrap(err, "resolving Cloudstack network information")
86-
}
8775

88-
// Address Isolated Networks.
89-
// Check if the passed network was an isolated network or the network was missing. In either case, create a
90-
// CloudStackIsolatedNetwork to manage the many intricacies and wait until CloudStackIsolatedNetwork is ready.
91-
if r.ReconciliationSubject.Spec.Network.ID == "" || r.ReconciliationSubject.Spec.Network.Type == infrav1.NetworkTypeIsolated {
92-
netName := r.ReconciliationSubject.Spec.Network.Name
93-
if res, err := r.GenerateIsolatedNetwork(netName)(); r.ShouldReturn(res, err) {
94-
return res, err
95-
} else if res, err := r.GetObjectByName(r.IsoNetMetaName(netName), r.IsoNet)(); r.ShouldReturn(res, err) {
96-
return res, err
97-
}
98-
if r.IsoNet.Name == "" {
99-
return r.RequeueWithMessage("Couldn't find isolated network.")
100-
}
101-
if !r.IsoNet.Status.Ready {
102-
return r.RequeueWithMessage("Isolated network dependency not ready.")
103-
}
104-
}
105-
r.ReconciliationSubject.Status.Ready = true
106-
return ctrl.Result{}, nil
107-
}
76+
// // Prevent premature deletion.
77+
// controllerutil.AddFinalizer(r.ReconciliationSubject, infrav1.)
78+
79+
// // Start by purely data fetching information about the zone and specified network.
80+
// if err := r.CSUser.ResolveZone(r.ReconciliationSubject); err != nil {
81+
// return ctrl.Result{}, errors.Wrap(err, "resolving CloudStack zone information")
82+
// }
83+
// if err := r.CSUser.ResolveNetworkForZone(r.ReconciliationSubject); err != nil &&
84+
// !csCtrlrUtils.ContainsNoMatchSubstring(err) {
85+
// return ctrl.Result{}, errors.Wrap(err, "resolving Cloudstack network information")
86+
// }
87+
88+
// // Address Isolated Networks.
89+
// // Check if the passed network was an isolated network or the network was missing. In either case, create a
90+
// // CloudStackIsolatedNetwork to manage the many intricacies and wait until CloudStackIsolatedNetwork is ready.
91+
// if r.ReconciliationSubject.Spec.Network.ID == "" || r.ReconciliationSubject.Spec.Network.Type == infrav1.NetworkTypeIsolated {
92+
// netName := r.ReconciliationSubject.Spec.Network.Name
93+
// if res, err := r.GenerateIsolatedNetwork(netName)(); r.ShouldReturn(res, err) {
94+
// return res, err
95+
// } else if res, err := r.GetObjectByName(r.IsoNetMetaName(netName), r.IsoNet)(); r.ShouldReturn(res, err) {
96+
// return res, err
97+
// }
98+
// if r.IsoNet.Name == "" {
99+
// return r.RequeueWithMessage("Couldn't find isolated network.")
100+
// }
101+
// if !r.IsoNet.Status.Ready {
102+
// return r.RequeueWithMessage("Isolated network dependency not ready.")
103+
// }
104+
// }
108105
r.ReconciliationSubject.Status.Ready = true
109106
return ctrl.Result{}, nil
110107
}

controllers/utils/base_reconciler.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"strings"
2323
"time"
2424

25+
"github.com/apache/cloudstack-go/cloudstack"
2526
"github.com/go-logr/logr"
2627
"github.com/hashicorp/go-multierror"
2728
"github.com/pkg/errors"
@@ -441,3 +442,25 @@ func (r *ReconciliationRunner) GetObjectByName(name string, target client.Object
441442
client.IgnoreNotFound(r.K8sClient.Get(r.RequestCtx, objectKey, target)), "failed to get object")
442443
}
443444
}
445+
446+
// NewClientFromSpec generates a new client from an existing client.
447+
// VerifySSL will be set to true if either the old or new configs is true.
448+
func (origC *client) NewClientFromSpec(cfg Config) (Client, error) {
449+
newC := &client{config: cfg}
450+
newC.config.VerifySSL = cfg.VerifySSL || origC.config.VerifySSL // Prefer the most secure setting given.
451+
if newC.config.APIURL == "" {
452+
newC.config.APIURL = origC.config.APIURL
453+
}
454+
455+
// The client returned from NewAsyncClient works in a synchronous way. On the other hand,
456+
// a client returned from NewClient works in an asynchronous way. Dive into the constructor definition
457+
// comments for more details
458+
newC.cs = cloudstack.NewAsyncClient(newC.config.APIURL, newC.config.APIKey, newC.config.SecretKey, newC.config.VerifySSL)
459+
newC.csAsync = cloudstack.NewClient(newC.config.APIURL, newC.config.APIKey, newC.config.SecretKey, newC.config.VerifySSL)
460+
461+
_, err := newC.cs.APIDiscovery.ListApis(newC.cs.APIDiscovery.NewListApisParams())
462+
if err != nil && strings.Contains(strings.ToLower(err.Error()), "i/o timeout") {
463+
return newC, errors.Wrap(err, "timeout while checking CloudStack API Client connectivity")
464+
}
465+
return newC, errors.Wrap(err, "checking CloudStack API Client connectivity")
466+
}

controllers/utils/failuredomains.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ package utils
1919
import (
2020
"strings"
2121

22+
corev1 "k8s.io/api/core/v1"
2223
infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2"
24+
"sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud"
2325

2426
"github.com/pkg/errors"
2527
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
@@ -78,3 +80,42 @@ func (r *ReconciliationRunner) GetFailureDomainssAndRequeueIfMissing(fds *infrav
7880
return ctrl.Result{}, nil
7981
}
8082
}
83+
84+
// AsFailureDomainUser uses the credentials specified in the failure domain to set the ReconciliationSubject's CSUser client.
85+
func (r *ReconciliationRunner) AsFailureDomainUser(fdSpec infrav1.CloudStackFailureDomainSpec) CloudStackReconcilerMethod {
86+
return func() (ctrl.Result, error) {
87+
endpointCredentials := &corev1.Secret{}
88+
key := client.ObjectKey{Name: fdSpec.ACSEndpoint.Name, Namespace: fdSpec.ACSEndpoint.Namespace}
89+
if err := r.K8sClient.Get(r.RequestCtx, key, endpointCredentials); err != nil {
90+
return ctrl.Result{}, errors.Wrapf(err, "getting ACSEndpoint secret with ref: %v", fdSpec.ACSEndpoint)
91+
}
92+
93+
94+
r.CSUser = r.CSClient
95+
cloud.NewClient()
96+
if r.CSCluster.Spec.Account != "" {
97+
user := &cloud.User{}
98+
user.Account.Domain.Path = r.CSCluster.Spec.Domain
99+
user.Account.Name = r.CSCluster.Spec.Account
100+
if found, err := r.CSClient.GetUserWithKeys(user); err != nil {
101+
return ctrl.Result{}, err
102+
} else if !found {
103+
return ctrl.Result{}, errors.Errorf("could not find sufficient user (with API keys) in domain/account %s/%s",
104+
r.CSCluster.Spec.Domain, r.CSCluster.Spec.Account)
105+
}
106+
cfg := cloud.Config{APIKey: user.APIKey, SecretKey: user.SecretKey}
107+
client, err := r.CSClient.NewClientFromSpec(cfg)
108+
if err != nil {
109+
return ctrl.Result{}, err
110+
}
111+
r.CSUser = client
112+
}
113+
114+
return ctrl.Result{}, nil
115+
}
116+
}
117+
118+
endpointCredentials.StringData["api-key"]
119+
endpointCredentials.StringData["api-url"]
120+
endpointCredentials.StringData["secret-key"]
121+
endpointCredentials.StringData["verify-ssl"]

main.go

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,13 @@ import (
2020
"fmt"
2121
"math/rand"
2222
"os"
23-
"strings"
2423
"time"
2524

2625
"k8s.io/klog/v2/klogr"
2726

2827
flag "github.com/spf13/pflag"
2928
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
3029

31-
"sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud"
32-
3330
goflag "flag"
3431

3532
_ "k8s.io/client-go/plugin/pkg/client/auth"
@@ -128,17 +125,6 @@ func main() {
128125

129126
ctrl.SetLogger(klogr.New())
130127

131-
// Setup CloudStack api client.
132-
client, err := cloud.NewClient(opts.CloudConfigFile)
133-
if err != nil {
134-
if !strings.Contains(strings.ToLower(err.Error()), "timeout") {
135-
setupLog.Error(err, "unable to start manager")
136-
os.Exit(1)
137-
}
138-
setupLog.Info("cannot connect to CloudStack via client at startup time. Pressing onward...")
139-
}
140-
setupLog.Info("CloudStack client initialized.")
141-
142128
// Create the controller manager.
143129
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
144130
Scheme: scheme,
@@ -162,8 +148,7 @@ func main() {
162148
base := utils.ReconcilerBase{
163149
K8sClient: mgr.GetClient(),
164150
BaseLogger: ctrl.Log.WithName("controllers"),
165-
Scheme: mgr.GetScheme(),
166-
CSClient: client}
151+
Scheme: mgr.GetScheme()}
167152

168153
setupReconcilers(base, mgr)
169154

pkg/cloud/client.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package cloud
1818

1919
import (
20+
"encoding/json"
2021
"strings"
2122

2223
"github.com/apache/cloudstack-go/v2/cloudstack"
@@ -47,10 +48,10 @@ type client struct {
4748

4849
// cloud-config ini structure.
4950
type Config struct {
50-
APIURL string `ini:"api-url"`
51-
APIKey string `ini:"api-key"`
52-
SecretKey string `ini:"secret-key"`
53-
VerifySSL bool `ini:"verify-ssl"`
51+
APIURL string `json:"api-url"`
52+
APIKey string `json:"api-key"`
53+
SecretKey string `json:"secret-key"`
54+
VerifySSL bool `json:"verify-ssl"`
5455
}
5556

5657
func NewClient(ccPath string) (Client, error) {
@@ -103,3 +104,22 @@ func NewClientFromCSAPIClient(cs *cloudstack.CloudStackClient) Client {
103104
c := &client{cs: cs, csAsync: cs}
104105
return c
105106
}
107+
108+
// Creates a new Cloud Client form a map of strings to strings.
109+
func NewClientFromMap(rawCfg map[string]string) (Client, error) {
110+
cfg := Config{VerifySSL: true} // Set sane defautl for verify-ssl.
111+
// Use JSON methods to enforce schema in parsing.
112+
if bytes, err := json.Marshal(rawCfg); err != nil {
113+
return nil, err
114+
} else if err := json.Unmarshal(bytes, &cfg); err != nil {
115+
return nil, err
116+
}
117+
118+
// The client returned from NewAsyncClient works in a synchronous way. On the other hand,
119+
// a client returned from NewClient works in an asynchronous way. Dive into the constructor definition
120+
// comments for more details
121+
c := &client{config: cfg}
122+
c.cs = cloudstack.NewAsyncClient(cfg.APIURL, cfg.APIKey, cfg.SecretKey, cfg.VerifySSL)
123+
c.csAsync = cloudstack.NewClient(cfg.APIURL, cfg.APIKey, cfg.SecretKey, cfg.VerifySSL)
124+
return c, nil
125+
}

0 commit comments

Comments
 (0)