Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions api/k8s/v1/k8s_helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package k8s

import (
"time"

"github.com/scaleway/scaleway-sdk-go/internal/async"
"github.com/scaleway/scaleway-sdk-go/internal/errors"
"github.com/scaleway/scaleway-sdk-go/scw"
)

var (
// RetryInterval is needed when running recorded tests (e.g. on scaleway-cli)
// it allows to execute the WaitFor funcs immediately
RetryInterval = defaultRetryInterval
)

const (
waitForClusterDefaultTimeout = time.Minute * 15
waitForPoolDefaultTimeout = time.Minute * 15
waitForNodeDefaultTimeout = time.Minute * 15
defaultRetryInterval = time.Second * 5
)

// WaitForClusterRequest is used by WaitForCluster method.
type WaitForClusterRequest struct {
ClusterID string
Region scw.Region
Status ClusterStatus
Timeout *time.Duration
}

// WaitForCluster waits for the cluster to be in a "terminal state" before returning.
func (s *API) WaitForCluster(req *WaitForClusterRequest) (*Cluster, error) {
timeout := waitForClusterDefaultTimeout
if req.Timeout != nil {
timeout = *req.Timeout
}
terminalStatus := map[ClusterStatus]struct{}{
ClusterStatusReady: {},
ClusterStatusLocked: {},
ClusterStatusDeleted: {},
ClusterStatusPoolRequired: {},
}

cluster, err := async.WaitSync(&async.WaitSyncConfig{
Get: func() (interface{}, bool, error) {
cluster, err := s.GetCluster(&GetClusterRequest{
ClusterID: req.ClusterID,
Region: req.Region,
})
if err != nil {
return nil, false, err
}

_, isTerminal := terminalStatus[cluster.Status]
return cluster, isTerminal, nil
},
Timeout: timeout,
IntervalStrategy: async.LinearIntervalStrategy(RetryInterval),
})
if err != nil {
return nil, errors.Wrap(err, "waiting for cluster failed")
}
return cluster.(*Cluster), nil
}

// WaitForPoolRequest is used by WaitForPool method.
type WaitForPoolRequest struct {
PoolID string
Region scw.Region
Timeout *time.Duration
}

// WaitForPool waits for a pool to be ready
func (s *API) WaitForPool(req *WaitForPoolRequest) (*Pool, error) {
terminalStatus := map[PoolStatus]struct{}{
PoolStatusReady: {},
PoolStatusWarning: {},
}

timeout := waitForPoolDefaultTimeout
if req.Timeout != nil {
timeout = *req.Timeout
}

pool, err := async.WaitSync(&async.WaitSyncConfig{
Get: func() (interface{}, bool, error) {
res, err := s.GetPool(&GetPoolRequest{
PoolID: req.PoolID,
Region: req.Region,
})

if err != nil {
return nil, false, err
}
_, isTerminal := terminalStatus[res.Status]

return res, isTerminal, nil
},
Timeout: timeout,
IntervalStrategy: async.LinearIntervalStrategy(RetryInterval),
})

return pool.(*Pool), err
}

// WaitForNodeRequest is used by WaitForNode method.
type WaitForNodeRequest struct {
NodeID string
Region scw.Region
Timeout *time.Duration
}

// WaitForNode waits for a Node to be ready
func (s *API) WaitForNode(req *WaitForNodeRequest) (*Node, error) {
terminalStatus := map[NodeStatus]struct{}{
NodeStatusCreationError: {},
NodeStatusReady: {},
}

timeout := waitForNodeDefaultTimeout
if req.Timeout != nil {
timeout = *req.Timeout
}

node, err := async.WaitSync(&async.WaitSyncConfig{
Get: func() (interface{}, bool, error) {
res, err := s.GetNode(&GetNodeRequest{
NodeID: req.NodeID,
Region: req.Region,
})

if err != nil {
return nil, false, err
}
_, isTerminal := terminalStatus[res.Status]

return res, isTerminal, nil
},
Timeout: timeout,
IntervalStrategy: async.LinearIntervalStrategy(RetryInterval),
})

return node.(*Node), err
}
118 changes: 118 additions & 0 deletions api/k8s/v1/kubeconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package k8s

import (
"io/ioutil"

"github.com/scaleway/scaleway-sdk-go/internal/errors"
"github.com/scaleway/scaleway-sdk-go/scw"
"gopkg.in/yaml.v2"
)

// Kubeconfig represents a kubernetes kubeconfig file
type Kubeconfig struct {
raw []byte
APIVersion string `yaml:"apiVersion"`
Kind string `yaml:"kind"`
CurrentContext string `yaml:"current-context"`
Clusters []*KubeconfigClusterWithName `yaml:"clusters"`
Contexts []*KubeconfigContextWithName `yaml:"contexts"`
Users []*KubeconfigUserWithName `yaml:"users"`
}

// KubeconfigUserWithName represents a named cluster in the kubeconfig file
type KubeconfigClusterWithName struct {
Name string `yaml:"name"`
Cluster KubeconfigCluster `yaml:"cluster"`
}

// KubeconfigCluster represents a cluster in the kubeconfig file
type KubeconfigCluster struct {
Server string `yaml:"server,omitempty"`
CertificateAuthorityData string `yaml:"certificate-authority-data,omitempty"`
}

// KubeconfigContextWithName represents a named context in the kubeconfig file
type KubeconfigContextWithName struct {
Name string `yaml:"name"`
Context KubeconfigContext `yaml:"context"`
}

// KubeconfigContext represents a context in the kubeconfig file
type KubeconfigContext struct {
Cluster string `yaml:"cluster"`
Namespace string `yaml:"namespace,omitempty"`
User string `yaml:"user"`
}

// KubeconfigUserWithName represents a named user in the kubeconfig file
type KubeconfigUserWithName struct {
Name string `yaml:"name"`
User KubeconfigUser `yaml:"user"`
}

// KubeconfigUser represents a user in the kubeconfig file
type KubeconfigUser struct {
ClientCertificateData string `yaml:"client-certificate-data,omitempty"`
ClientKeyData string `yaml:"client-key-data,omitempty"`
Password string `yaml:"password,omitempty"`
Username string `yaml:"username,omitempty"`
Token string `yaml:"token,omitempty"`
}

// GetRaw returns the raw bytes of the kubeconfig
func (k *Kubeconfig) GetRaw() []byte {
return k.raw
}

// GetServer returns the server URL of the cluster in the kubeconfig
func (k *Kubeconfig) GetServer() (string, error) {
if len(k.Clusters) != 1 {
return "", errors.New("kubeconfig should have only one cluster")
}

return k.Clusters[0].Cluster.Server, nil
}

// GetCertificateAuthorityData returns the server certificate authority data of the cluster in the kubeconfig
func (k *Kubeconfig) GetCertificateAuthorityData() (string, error) {
if len(k.Clusters) != 1 {
return "", errors.New("kubeconfig should have only one cluster")
}

return k.Clusters[0].Cluster.CertificateAuthorityData, nil
}

// GetToken returns the token for the cluster in the kubeconfig
func (k *Kubeconfig) GetToken() (string, error) {
if len(k.Users) != 1 {
return "", errors.New("kubeconfig should have only one user")
}

return k.Users[0].User.Token, nil
}

// GetClusterKubeConfig downloads the kubeconfig for the given cluster
func (s *API) GetClusterKubeConfig(req *GetClusterKubeConfigRequest, opts ...scw.RequestOption) (*Kubeconfig, error) {
kubeconfigFile, err := s.getClusterKubeConfig(&GetClusterKubeConfigRequest{
Region: req.Region,
ClusterID: req.ClusterID,
})
if err != nil {
return nil, errors.Wrap(err, "error getting cluster kubeconfig")
}

kubeconfigContent, err := ioutil.ReadAll(kubeconfigFile.Content)
if err != nil {
return nil, errors.Wrap(err, "error reading kubeconfig content")
}

var kubeconfig Kubeconfig
err = yaml.Unmarshal(kubeconfigContent, &kubeconfig)
if err != nil {
return nil, errors.Wrap(err, "error unmarshaling kubeconfig")
}

kubeconfig.raw = kubeconfigContent

return &kubeconfig, nil
}