github.com/IBM-Cloud/bluemix-go@v0.0.0-20240314082800-4e02a69b84b2/api/container/containerv1/clusters.go (about)

     1  package containerv1
     2  
     3  import (
     4  	"crypto/sha256"
     5  	"errors"
     6  	"fmt"
     7  	"html/template"
     8  	"io/ioutil"
     9  	"os"
    10  	"path"
    11  	"path/filepath"
    12  	"strings"
    13  
    14  	"gopkg.in/yaml.v2"
    15  
    16  	"github.com/IBM-Cloud/bluemix-go/client"
    17  	"github.com/IBM-Cloud/bluemix-go/helpers"
    18  	"github.com/IBM-Cloud/bluemix-go/trace"
    19  )
    20  
    21  //ClusterInfo ...
    22  type ClusterInfo struct {
    23  	CreatedDate                   string   `json:"createdDate"`
    24  	DataCenter                    string   `json:"dataCenter"`
    25  	ID                            string   `json:"id"`
    26  	IngressHostname               string   `json:"ingressHostname"`
    27  	IngressSecretName             string   `json:"ingressSecretName"`
    28  	Location                      string   `json:"location"`
    29  	MasterKubeVersion             string   `json:"masterKubeVersion"`
    30  	ModifiedDate                  string   `json:"modifiedDate"`
    31  	Name                          string   `json:"name"`
    32  	Region                        string   `json:"region"`
    33  	ResourceGroupID               string   `json:"resourceGroup"`
    34  	ResourceGroupName             string   `json:"resourceGroupName"`
    35  	ServerURL                     string   `json:"serverURL"`
    36  	MasterURL                     string   `json:"masterURL"` // vpc cluster serverURL is empty
    37  	State                         string   `json:"state"`
    38  	OrgID                         string   `json:"logOrg"`
    39  	OrgName                       string   `json:"logOrgName"`
    40  	SpaceID                       string   `json:"logSpace"`
    41  	SpaceName                     string   `json:"logSpaceName"`
    42  	IsPaid                        bool     `json:"isPaid"`
    43  	IsTrusted                     bool     `json:"isTrusted"`
    44  	WorkerCount                   int      `json:"workerCount"`
    45  	Vlans                         []Vlan   `json:"vlans"`
    46  	Addons                        []Addon  `json:"addons"`
    47  	OwnerEmail                    string   `json:"ownerEmail"`
    48  	APIUser                       string   `json:"apiUser"`
    49  	MonitoringURL                 string   `json:"monitoringURL"`
    50  	DisableAutoUpdate             bool     `json:"disableAutoUpdate"`
    51  	EtcdPort                      string   `json:"etcdPort"`
    52  	MasterStatus                  string   `json:"masterStatus"`
    53  	MasterStatusModifiedDate      string   `json:"masterStatusModifiedDate"`
    54  	KeyProtectEnabled             bool     `json:"keyProtectEnabled"`
    55  	WorkerZones                   []string `json:"workerZones"`
    56  	PullSecretApplied             bool     `json:"pullSecretApplied"`
    57  	CRN                           string   `json:"crn"`
    58  	PrivateServiceEndpointEnabled bool     `json:"privateServiceEndpointEnabled"`
    59  	PrivateServiceEndpointURL     string   `json:"privateServiceEndpointURL"`
    60  	PublicServiceEndpointEnabled  bool     `json:"publicServiceEndpointEnabled"`
    61  	PublicServiceEndpointURL      string   `json:"publicServiceEndpointURL"`
    62  	Type                          string   `json:"type"`
    63  	Provider                      string   `json:"provider"`
    64  	PodSubnet                     string   `json:"podSubnet"`
    65  	ServiceSubnet                 string   `json:"serviceSubnet"`
    66  	ImageSecurityEnabled          bool     `json:"imageSecurityEnabled"`
    67  }
    68  
    69  // ClusterUpdateParam ...
    70  type ClusterUpdateParam struct {
    71  	Action  string `json:"action"`
    72  	Force   bool   `json:"force"`
    73  	Version string `json:"version"`
    74  }
    75  
    76  //ClusterKeyInfo ...
    77  type ClusterKeyInfo struct {
    78  	AdminKey             string `json:"admin-key"`
    79  	Admin                string `json:"admin"`
    80  	ClusterCACertificate string `json:"cluster-ca-certificate"`
    81  	Host                 string `json:"host"`
    82  	Token                string `json:"idtoken"`
    83  	FilePath             string `json:"filepath"`
    84  }
    85  
    86  //ConfigFileOpenshift Openshift .yml Structure
    87  type ConfigFileOpenshift struct {
    88  	Clusters []struct {
    89  		Name    string `yaml:"name"`
    90  		Cluster struct {
    91  			Server string `yaml:"server"`
    92  		} `yaml:"cluster"`
    93  	} `yaml:"clusters"`
    94  	Users []struct {
    95  		Name string `yaml:"name"`
    96  		User struct {
    97  			Token string `yaml:"token"`
    98  		}
    99  	}
   100  }
   101  
   102  // ConfigFile ...
   103  type ConfigFile struct {
   104  	Clusters []struct {
   105  		Name    string `yaml:"name"`
   106  		Cluster struct {
   107  			Server string `yaml:"server"`
   108  		} `yaml:"cluster"`
   109  	} `yaml:"clusters"`
   110  	Users []struct {
   111  		Name string `yaml:"name"`
   112  		User struct {
   113  			AuthProvider struct {
   114  				Config struct {
   115  					IDToken string `yaml:"id-token"`
   116  				} `yaml:"config"`
   117  			} `yaml:"auth-provider"`
   118  		} `yaml:"user"`
   119  	} `yaml:"users"`
   120  }
   121  
   122  //Vlan ...
   123  type Vlan struct {
   124  	ID      string `json:"id"`
   125  	Subnets []struct {
   126  		Cidr     string   `json:"cidr"`
   127  		ID       string   `json:"id"`
   128  		Ips      []string `json:"ips"`
   129  		IsByOIP  bool     `json:"is_byoip"`
   130  		IsPublic bool     `json:"is_public"`
   131  	}
   132  	Zone   string `json:"zone"`
   133  	Region string `json:"region"`
   134  }
   135  
   136  //Addon ...
   137  type Addon struct {
   138  	Name    string `json:"name"`
   139  	Enabled bool   `json:"enabled"`
   140  }
   141  
   142  //ClusterCreateResponse ...
   143  type ClusterCreateResponse struct {
   144  	ID string
   145  }
   146  
   147  // MasterAPIServer describes the state to put the Master API server into
   148  // swagger:model
   149  type MasterAPIServer struct {
   150  	Action string `json:"action" binding:"required" description:"The action to perform on the API Server"`
   151  }
   152  
   153  //ClusterTargetHeader ...
   154  type ClusterTargetHeader struct {
   155  	OrgID         string
   156  	SpaceID       string
   157  	AccountID     string
   158  	Region        string
   159  	ResourceGroup string
   160  }
   161  
   162  const (
   163  	orgIDHeader         = "X-Auth-Resource-Org"
   164  	spaceIDHeader       = "X-Auth-Resource-Space"
   165  	accountIDHeader     = "X-Auth-Resource-Account"
   166  	slUserNameHeader    = "X-Auth-Softlayer-Username"
   167  	slAPIKeyHeader      = "X-Auth-Softlayer-APIKey"
   168  	regionHeader        = "X-Region"
   169  	resourceGroupHeader = "X-Auth-Resource-Group"
   170  )
   171  
   172  //ToMap ...
   173  func (c ClusterTargetHeader) ToMap() map[string]string {
   174  	m := make(map[string]string, 3)
   175  	m[orgIDHeader] = c.OrgID
   176  	m[spaceIDHeader] = c.SpaceID
   177  	m[accountIDHeader] = c.AccountID
   178  	m[regionHeader] = c.Region
   179  	m[resourceGroupHeader] = c.ResourceGroup
   180  	return m
   181  }
   182  
   183  //ClusterSoftlayerHeader ...
   184  type ClusterSoftlayerHeader struct {
   185  	SoftLayerUsername string
   186  	SoftLayerAPIKey   string
   187  }
   188  
   189  //ToMap ...
   190  func (c ClusterSoftlayerHeader) ToMap() map[string]string {
   191  	m := make(map[string]string, 2)
   192  	m[slAPIKeyHeader] = c.SoftLayerAPIKey
   193  	m[slUserNameHeader] = c.SoftLayerUsername
   194  	return m
   195  }
   196  
   197  //ClusterCreateRequest ...
   198  type ClusterCreateRequest struct {
   199  	GatewayEnabled               bool   `json:"GatewayEnabled" description:"true for gateway enabled cluster"`
   200  	Datacenter                   string `json:"dataCenter" description:"The worker's data center"`
   201  	OperatingSystem              string `json:"operatingSystem,omitempty"`
   202  	Isolation                    string `json:"isolation" description:"Can be 'public' or 'private'"`
   203  	MachineType                  string `json:"machineType" description:"The worker's machine type"`
   204  	Name                         string `json:"name" binding:"required" description:"The cluster's name"`
   205  	PrivateVlan                  string `json:"privateVlan" description:"The worker's private vlan"`
   206  	PublicVlan                   string `json:"publicVlan" description:"The worker's public vlan"`
   207  	WorkerNum                    int    `json:"workerNum,omitempty" binding:"required" description:"The number of workers"`
   208  	NoSubnet                     bool   `json:"noSubnet" description:"Indicate whether portable subnet should be ordered for user"`
   209  	MasterVersion                string `json:"masterVersion,omitempty" description:"Desired version of the requested master"`
   210  	Prefix                       string `json:"prefix,omitempty" description:"hostname prefix for new workers"`
   211  	DiskEncryption               bool   `json:"diskEncryption" description:"disable encryption on a worker"`
   212  	PrivateEndpointEnabled       bool   `json:"privateSeviceEndpoint"`
   213  	PublicEndpointEnabled        bool   `json:"publicServiceEndpoint"`
   214  	DisableAutoUpdate            bool   `json:"disableAutoUpdate"`
   215  	DefaultWorkerPoolName        string `json:"defaultWorkerPoolName" description:"The name of default workerpool"`
   216  	PodSubnet                    string `json:"podSubnet"`
   217  	ServiceSubnet                string `json:"serviceSubnet"`
   218  	DefaultWorkerPoolEntitlement string `json:"defaultWorkerPoolEntitlement" description:"Additional licence/entitlement for the default worker pool"`
   219  }
   220  
   221  // ServiceBindRequest ...
   222  type ServiceBindRequest struct {
   223  	ClusterNameOrID         string
   224  	ServiceInstanceNameOrID string `json:"serviceInstanceGUID" binding:"required"`
   225  	NamespaceID             string `json:"namespaceID" binding:"required"`
   226  	Role                    string `json:"role"`
   227  	ServiceKeyJSON          string `json:"serviceKeyJSON"`
   228  	ServiceKeyGUID          string `json:"serviceKeyGUID"`
   229  }
   230  
   231  // ServiceBindResponse ...
   232  type ServiceBindResponse struct {
   233  	ServiceInstanceGUID string `json:"serviceInstanceGUID" binding:"required"`
   234  	NamespaceID         string `json:"namespaceID" binding:"required"`
   235  	SecretName          string `json:"secretName"`
   236  	Binding             string `json:"binding"`
   237  }
   238  
   239  //BoundService ...
   240  type BoundService struct {
   241  	ServiceName    string `json:"servicename"`
   242  	ServiceID      string `json:"serviceid"`
   243  	ServiceKeyName string `json:"servicekeyname"`
   244  	Namespace      string `json:"namespace"`
   245  }
   246  
   247  // UpdateWorkerCommand ....
   248  // swagger:model
   249  type UpdateWorkerCommand struct {
   250  	Action string `json:"action" binding:"required" description:"Action to perform of the worker"`
   251  	// Setting force flag to true will ignore if the master is unavailable during 'os_reboot" and 'reload' action
   252  	Force bool `json:"force,omitempty"`
   253  }
   254  
   255  //BoundServices ..
   256  type BoundServices []BoundService
   257  
   258  //Clusters interface
   259  type Clusters interface {
   260  	Create(params ClusterCreateRequest, target ClusterTargetHeader) (ClusterCreateResponse, error)
   261  	List(target ClusterTargetHeader) ([]ClusterInfo, error)
   262  	Update(name string, params ClusterUpdateParam, target ClusterTargetHeader) error
   263  	UpdateClusterWorker(clusterNameOrID string, workerID string, params UpdateWorkerCommand, target ClusterTargetHeader) error
   264  	UpdateClusterWorkers(clusterNameOrID string, workerIDs []string, params UpdateWorkerCommand, target ClusterTargetHeader) error
   265  	Delete(name string, target ClusterTargetHeader, deleteDependencies ...bool) error
   266  	Find(name string, target ClusterTargetHeader) (ClusterInfo, error)
   267  	FindWithOutShowResources(name string, target ClusterTargetHeader) (ClusterInfo, error)
   268  	FindWithOutShowResourcesCompatible(name string, target ClusterTargetHeader) (ClusterInfo, error)
   269  	GetClusterConfig(name, homeDir string, admin bool, target ClusterTargetHeader) (string, error)
   270  	GetClusterConfigDetail(name, homeDir string, admin bool, target ClusterTargetHeader) (ClusterKeyInfo, error)
   271  	StoreConfig(name, baseDir string, admin bool, createCalicoConfig bool, target ClusterTargetHeader) (string, string, error)
   272  	StoreConfigDetail(name, baseDir string, admin bool, createCalicoConfig bool, target ClusterTargetHeader) (string, ClusterKeyInfo, error)
   273  	UnsetCredentials(target ClusterTargetHeader) error
   274  	SetCredentials(slUsername, slAPIKey string, target ClusterTargetHeader) error
   275  	BindService(params ServiceBindRequest, target ClusterTargetHeader) (ServiceBindResponse, error)
   276  	UnBindService(clusterNameOrID, namespaceID, serviceInstanceGUID string, target ClusterTargetHeader) error
   277  	ListServicesBoundToCluster(clusterNameOrID, namespace string, target ClusterTargetHeader) (BoundServices, error)
   278  	FindServiceBoundToCluster(clusterNameOrID, serviceName, namespace string, target ClusterTargetHeader) (BoundService, error)
   279  	RefreshAPIServers(clusterNameOrID string, target ClusterTargetHeader) error
   280  	FetchOCTokenForKubeConfig(kubeConfig []byte, clusterInfo *ClusterInfo, skipSSLVerification bool) ([]byte, error)
   281  }
   282  
   283  type clusters struct {
   284  	client *client.Client
   285  }
   286  
   287  func newClusterAPI(c *client.Client) Clusters {
   288  	return &clusters{
   289  		client: c,
   290  	}
   291  }
   292  
   293  func (r *ClusterInfo) IsStagingSatelliteCluster() bool {
   294  
   295  	return strings.Index(r.ServerURL, "stg") > 0 && r.Provider == "satellite"
   296  }
   297  
   298  //Create ...
   299  func (r *clusters) Create(params ClusterCreateRequest, target ClusterTargetHeader) (ClusterCreateResponse, error) {
   300  	var cluster ClusterCreateResponse
   301  	_, err := r.client.Post("/v1/clusters", params, &cluster, target.ToMap())
   302  	return cluster, err
   303  }
   304  
   305  //Update ...
   306  func (r *clusters) Update(name string, params ClusterUpdateParam, target ClusterTargetHeader) error {
   307  	rawURL := fmt.Sprintf("/v1/clusters/%s", name)
   308  	_, err := r.client.Put(rawURL, params, nil, target.ToMap())
   309  	return err
   310  }
   311  
   312  // UpdateClusterWorker ...
   313  func (r *clusters) UpdateClusterWorker(clusterNameOrID string, workerID string, params UpdateWorkerCommand, target ClusterTargetHeader) error {
   314  	rawURL := fmt.Sprintf("/v1/clusters/%s/workers/%s", clusterNameOrID, workerID)
   315  	// Make the request
   316  	_, err := r.client.Put(rawURL, params, nil, target.ToMap())
   317  	return err
   318  }
   319  
   320  // UpdateClusterWorkers updates a batch of workers in parallel
   321  func (r *clusters) UpdateClusterWorkers(clusterNameOrID string, workerIDs []string, params UpdateWorkerCommand, target ClusterTargetHeader) error {
   322  	for _, workerID := range workerIDs {
   323  		if workerID == "" {
   324  			return errors.New("workere id's can not be empty")
   325  		}
   326  		err := r.UpdateClusterWorker(clusterNameOrID, workerID, params, target)
   327  		if err != nil {
   328  			return err
   329  		}
   330  
   331  	}
   332  	return nil
   333  }
   334  
   335  //Delete ...
   336  func (r *clusters) Delete(name string, target ClusterTargetHeader, deleteDependencies ...bool) error {
   337  	var rawURL string
   338  	if len(deleteDependencies) != 0 {
   339  		rawURL = fmt.Sprintf("/v1/clusters/%s?deleteResources=%t", name, deleteDependencies[0])
   340  	} else {
   341  		rawURL = fmt.Sprintf("/v1/clusters/%s", name)
   342  	}
   343  	_, err := r.client.Delete(rawURL, target.ToMap())
   344  	return err
   345  }
   346  
   347  //List ...
   348  func (r *clusters) List(target ClusterTargetHeader) ([]ClusterInfo, error) {
   349  	clusters := []ClusterInfo{}
   350  	_, err := r.client.Get("/v1/clusters", &clusters, target.ToMap())
   351  	if err != nil {
   352  		return nil, err
   353  	}
   354  
   355  	return clusters, err
   356  }
   357  
   358  //Find ...
   359  func (r *clusters) Find(name string, target ClusterTargetHeader) (ClusterInfo, error) {
   360  	rawURL := fmt.Sprintf("/v1/clusters/%s?showResources=true", name)
   361  	cluster := ClusterInfo{}
   362  	_, err := r.client.Get(rawURL, &cluster, target.ToMap())
   363  	if err != nil {
   364  		return cluster, err
   365  	}
   366  
   367  	return cluster, err
   368  }
   369  
   370  //FindWithOutShowResources ...
   371  func (r *clusters) FindWithOutShowResources(name string, target ClusterTargetHeader) (ClusterInfo, error) {
   372  	rawURL := fmt.Sprintf("/v1/clusters/%s", name)
   373  	cluster := ClusterInfo{}
   374  	_, err := r.client.Get(rawURL, &cluster, target.ToMap())
   375  	if err != nil {
   376  		return cluster, err
   377  	}
   378  
   379  	return cluster, err
   380  }
   381  
   382  //FindWithOutShowResourcesCompatible ...
   383  func (r *clusters) FindWithOutShowResourcesCompatible(name string, target ClusterTargetHeader) (ClusterInfo, error) {
   384  	rawURL := fmt.Sprintf("/v2/getCluster?v1-compatible&cluster=%s", name)
   385  	cluster := ClusterInfo{}
   386  	_, err := r.client.Get(rawURL, &cluster, target.ToMap())
   387  	if err != nil {
   388  		return cluster, err
   389  	}
   390  	// Handle VPC cluster.  ServerURL is blank for v2/vpc clusters
   391  	if cluster.ServerURL == "" {
   392  		cluster.ServerURL = cluster.MasterURL
   393  	}
   394  	return cluster, err
   395  }
   396  
   397  //GetClusterConfig ...
   398  func (r *clusters) GetClusterConfig(name, dir string, admin bool, target ClusterTargetHeader) (string, error) {
   399  	if !helpers.FileExists(dir) {
   400  		return "", fmt.Errorf("Path: %q, to download the config doesn't exist", dir)
   401  	}
   402  	rawURL := fmt.Sprintf("/v1/clusters/%s/config", name)
   403  	if admin {
   404  		rawURL += "/admin"
   405  	}
   406  	resultDir := ComputeClusterConfigDir(dir, name, admin)
   407  	const kubeConfigName = "config.yml"
   408  	err := os.MkdirAll(resultDir, 0755)
   409  	if err != nil {
   410  		return "", fmt.Errorf("Error creating directory to download the cluster config")
   411  	}
   412  	downloadPath := filepath.Join(resultDir, "config.zip")
   413  	trace.Logger.Println("Will download the kubeconfig at", downloadPath)
   414  
   415  	var out *os.File
   416  	if out, err = os.Create(downloadPath); err != nil {
   417  		return "", err
   418  	}
   419  	defer out.Close()
   420  	defer helpers.RemoveFile(downloadPath)
   421  	_, err = r.client.Get(rawURL, out, target.ToMap())
   422  	if err != nil {
   423  		return "", err
   424  	}
   425  	trace.Logger.Println("Downloaded the kubeconfig at", downloadPath)
   426  	if err = helpers.Unzip(downloadPath, resultDir); err != nil {
   427  		return "", err
   428  	}
   429  	defer helpers.RemoveFilesWithPattern(resultDir, "[^(.yml)|(.pem)]$")
   430  	var kubedir, kubeyml string
   431  	files, _ := ioutil.ReadDir(resultDir)
   432  	for _, f := range files {
   433  		if f.IsDir() && strings.HasPrefix(f.Name(), "kube") {
   434  			kubedir = filepath.Join(resultDir, f.Name())
   435  			files, _ := ioutil.ReadDir(kubedir)
   436  			for _, f := range files {
   437  				old := filepath.Join(kubedir, f.Name())
   438  				new := filepath.Join(kubedir, "../", f.Name())
   439  				if strings.HasSuffix(f.Name(), ".yml") {
   440  					new = filepath.Join(path.Clean(kubedir), "../", path.Clean(kubeConfigName))
   441  					kubeyml = new
   442  				}
   443  				err := os.Rename(old, new)
   444  				if err != nil {
   445  					return "", fmt.Errorf("Couldn't rename: %q", err)
   446  				}
   447  			}
   448  			break
   449  		}
   450  	}
   451  	if kubedir == "" {
   452  		return "", errors.New("Unable to locate kube config in zip archive")
   453  	}
   454  
   455  	// Block to add token for openshift clusters (This can be temporary until iks team handles openshift clusters)
   456  	clusterInfo, err := r.FindWithOutShowResourcesCompatible(name, target)
   457  	if err != nil {
   458  		// Assuming an error means that this is a vpc cluster, and we're returning existing kubeconfig
   459  		// When we add support for vpcs on openshift clusters, we may want revisit this
   460  		return filepath.Abs(kubeyml)
   461  	}
   462  
   463  	if clusterInfo.Type == "openshift" {
   464  		trace.Logger.Println("Debug: type is openshift trying login to get token")
   465  		var yamlConfig []byte
   466  		if yamlConfig, err = ioutil.ReadFile(kubeyml); err != nil {
   467  			return "", err
   468  		}
   469  		yamlConfig, err = r.FetchOCTokenForKubeConfig(yamlConfig, &clusterInfo, clusterInfo.IsStagingSatelliteCluster())
   470  		if err != nil {
   471  			return "", err
   472  		}
   473  		err = ioutil.WriteFile(kubeyml, yamlConfig, 0644) // 0644 is irrelevant here, since file already exists.
   474  		if err != nil {
   475  			return "", err
   476  		}
   477  	}
   478  
   479  	return filepath.Abs(kubeyml)
   480  }
   481  
   482  //GetClusterConfigDetail ...
   483  func (r *clusters) GetClusterConfigDetail(name, dir string, admin bool, target ClusterTargetHeader) (ClusterKeyInfo, error) {
   484  	clusterkey := ClusterKeyInfo{}
   485  	if !helpers.FileExists(dir) {
   486  		return clusterkey, fmt.Errorf("Path: %q, to download the config doesn't exist", dir)
   487  	}
   488  	rawURL := fmt.Sprintf("/v1/clusters/%s/config", name)
   489  	if admin {
   490  		rawURL += "/admin"
   491  	}
   492  	resultDir := ComputeClusterConfigDir(dir, name, admin)
   493  	const kubeConfigName = "config.yml"
   494  	err := os.MkdirAll(resultDir, 0755)
   495  	if err != nil {
   496  		return clusterkey, fmt.Errorf("Error creating directory to download the cluster config")
   497  	}
   498  	downloadPath := filepath.Join(resultDir, "config.zip")
   499  	trace.Logger.Println("Will download the kubeconfig at", downloadPath)
   500  
   501  	var out *os.File
   502  	if out, err = os.Create(downloadPath); err != nil {
   503  		return clusterkey, err
   504  	}
   505  	defer out.Close()
   506  	defer helpers.RemoveFile(downloadPath)
   507  	_, err = r.client.Get(rawURL, out, target.ToMap())
   508  	if err != nil {
   509  		return clusterkey, err
   510  	}
   511  	trace.Logger.Println("Downloaded the kubeconfig at", downloadPath)
   512  	if err = helpers.Unzip(downloadPath, resultDir); err != nil {
   513  		return clusterkey, err
   514  	}
   515  	defer helpers.RemoveFilesWithPattern(resultDir, "[^(.yml)|(.pem)]$")
   516  	var kubedir, kubeyml string
   517  	files, _ := ioutil.ReadDir(resultDir)
   518  	for _, f := range files {
   519  		if f.IsDir() && strings.HasPrefix(f.Name(), "kube") {
   520  			kubedir = filepath.Join(resultDir, f.Name())
   521  			files, _ := ioutil.ReadDir(kubedir)
   522  			for _, f := range files {
   523  				fileContent, _ := ioutil.ReadFile(kubedir + "/" + f.Name())
   524  				if f.Name() == "admin-key.pem" {
   525  					clusterkey.AdminKey = string(fileContent)
   526  				}
   527  				if f.Name() == "admin.pem" {
   528  					clusterkey.Admin = string(fileContent)
   529  				}
   530  				if strings.HasPrefix(f.Name(), "ca-") && strings.HasSuffix(f.Name(), ".pem") {
   531  					clusterkey.ClusterCACertificate = string(fileContent)
   532  				}
   533  				old := filepath.Join(kubedir, f.Name())
   534  				new := filepath.Join(kubedir, "../", f.Name())
   535  				if strings.HasSuffix(f.Name(), ".yml") {
   536  					new = filepath.Join(path.Clean(kubedir), "../", path.Clean(kubeConfigName))
   537  					kubeyml = new
   538  				}
   539  				err := os.Rename(old, new)
   540  				if err != nil {
   541  					return clusterkey, fmt.Errorf("Couldn't rename: %q", err)
   542  				}
   543  			}
   544  			break
   545  		}
   546  	}
   547  	if kubedir == "" {
   548  		return clusterkey, errors.New("Unable to locate kube config in zip archive")
   549  	}
   550  
   551  	kubefile, _ := ioutil.ReadFile(kubeyml)
   552  	var yamlConfig ConfigFile
   553  	err = yaml.Unmarshal(kubefile, &yamlConfig)
   554  	if err != nil {
   555  		fmt.Printf("Error parsing YAML file: %s\n", err)
   556  	}
   557  	if len(yamlConfig.Clusters) != 0 {
   558  		clusterkey.Host = yamlConfig.Clusters[0].Cluster.Server
   559  	}
   560  	if len(yamlConfig.Users) != 0 {
   561  		clusterkey.Token = yamlConfig.Users[0].User.AuthProvider.Config.IDToken
   562  	}
   563  
   564  	// Block to add token for openshift clusters (This can be temporary until iks team handles openshift clusters)
   565  	clusterInfo, err := r.FindWithOutShowResourcesCompatible(name, target)
   566  	if err != nil {
   567  		// Assuming an error means that this is a vpc cluster, and we're returning existing kubeconfig
   568  		// When we add support for vpcs on openshift clusters, we may want revisit this
   569  		clusterkey.FilePath, _ = filepath.Abs(kubeyml)
   570  		return clusterkey, err
   571  	}
   572  
   573  	if clusterInfo.Type == "openshift" {
   574  		trace.Logger.Println("Debug: type is openshift trying login to get token")
   575  		var yamlConfig []byte
   576  		if yamlConfig, err = ioutil.ReadFile(kubeyml); err != nil {
   577  			return clusterkey, err
   578  		}
   579  		yamlConfig, err = r.FetchOCTokenForKubeConfig(yamlConfig, &clusterInfo, clusterInfo.IsStagingSatelliteCluster())
   580  		if err != nil {
   581  			return clusterkey, err
   582  		}
   583  		err = ioutil.WriteFile(kubeyml, yamlConfig, 0644) // 0644 is irrelevant here, since file already exists.
   584  		if err != nil {
   585  			return clusterkey, err
   586  		}
   587  		openshiftyml, _ := ioutil.ReadFile(kubeyml)
   588  		var openshiftyaml ConfigFileOpenshift
   589  		err = yaml.Unmarshal(openshiftyml, &openshiftyaml)
   590  		if err != nil {
   591  			fmt.Printf("Error parsing YAML file: %s\n", err)
   592  		}
   593  		openshiftusers := openshiftyaml.Users
   594  		for _, usr := range openshiftusers {
   595  			if strings.HasPrefix(usr.Name, "IAM") {
   596  				clusterkey.Token = usr.User.Token
   597  			}
   598  		}
   599  		if len(openshiftyaml.Clusters) != 0 {
   600  			clusterkey.Host = openshiftyaml.Clusters[0].Cluster.Server
   601  		}
   602  		clusterkey.ClusterCACertificate = ""
   603  
   604  	}
   605  	clusterkey.FilePath, _ = filepath.Abs(kubeyml)
   606  	return clusterkey, err
   607  }
   608  
   609  // StoreConfig ...
   610  func (r *clusters) StoreConfig(name, dir string, admin, createCalicoConfig bool, target ClusterTargetHeader) (string, string, error) {
   611  	var calicoConfig string
   612  	if !helpers.FileExists(dir) {
   613  		return "", "", fmt.Errorf("Path: %q, to download the config doesn't exist", dir)
   614  	}
   615  	rawURL := fmt.Sprintf("/v1/clusters/%s/config", name)
   616  	if admin {
   617  		rawURL += "/admin"
   618  	}
   619  	if createCalicoConfig {
   620  		rawURL += "?createNetworkConfig=true"
   621  	}
   622  	resultDir := ComputeClusterConfigDir(dir, name, admin)
   623  	err := os.MkdirAll(resultDir, 0755)
   624  	if err != nil {
   625  		return "", "", fmt.Errorf("Error creating directory to download the cluster config")
   626  	}
   627  	downloadPath := filepath.Join(resultDir, "config.zip")
   628  	trace.Logger.Println("Will download the kubeconfig at", downloadPath)
   629  
   630  	var out *os.File
   631  	if out, err = os.Create(downloadPath); err != nil {
   632  		return "", "", err
   633  	}
   634  	defer out.Close()
   635  	defer helpers.RemoveFile(downloadPath)
   636  	_, err = r.client.Get(rawURL, out, target.ToMap())
   637  	if err != nil {
   638  		return "", "", err
   639  	}
   640  	trace.Logger.Println("Downloaded the kubeconfig at", downloadPath)
   641  	if err = helpers.Unzip(downloadPath, resultDir); err != nil {
   642  		return "", "", err
   643  	}
   644  	trace.Logger.Println("Downloaded the kubec", resultDir)
   645  
   646  	unzipConfigPath, err := kubeConfigDir(resultDir)
   647  	if err != nil {
   648  		return "", "", err
   649  	}
   650  	trace.Logger.Println("Located unzipped directory: ", unzipConfigPath)
   651  	files, _ := ioutil.ReadDir(unzipConfigPath)
   652  	for _, f := range files {
   653  		old := filepath.Join(unzipConfigPath, f.Name())
   654  		new := filepath.Join(unzipConfigPath, "../", f.Name())
   655  		err := os.Rename(old, new)
   656  		if err != nil {
   657  			return "", "", fmt.Errorf("Couldn't rename: %q", err)
   658  		}
   659  	}
   660  	err = os.RemoveAll(unzipConfigPath)
   661  	if err != nil {
   662  		return "", "", err
   663  	}
   664  	// Locate the yaml file and return the new path
   665  	baseDirFiles, err := ioutil.ReadDir(resultDir)
   666  	if err != nil {
   667  		return "", "", err
   668  	}
   669  
   670  	if createCalicoConfig {
   671  		// Proccess calico golang template file if it exists
   672  		calicoConfig, err = GenerateCalicoConfig(resultDir)
   673  		if err != nil {
   674  			return "", "", err
   675  		}
   676  	}
   677  	var kubeconfigFileName string
   678  	for _, baseDirFile := range baseDirFiles {
   679  		if strings.Contains(baseDirFile.Name(), ".yml") {
   680  			kubeconfigFileName = fmt.Sprintf("%s/%s", resultDir, baseDirFile.Name())
   681  			break
   682  		}
   683  	}
   684  	if kubeconfigFileName == "" {
   685  		return "", "", errors.New("Unable to locate kube config in zip archive")
   686  	}
   687  
   688  	// Block to add token for openshift clusters (This can be temporary until iks team handles openshift clusters)
   689  	clusterInfo, err := r.FindWithOutShowResourcesCompatible(name, target)
   690  	if err != nil {
   691  		// Assuming an error means that this is a vpc cluster, and we're returning existing kubeconfig
   692  		// When we add support for vpcs on openshift clusters, we may want revisit this
   693  		return kubeconfigFileName, calicoConfig, nil
   694  	}
   695  
   696  	if clusterInfo.Type == "openshift" {
   697  		trace.Logger.Println("Cluster Type is openshift trying login to get token")
   698  		var yamlConfig []byte
   699  		if yamlConfig, err = ioutil.ReadFile(kubeconfigFileName); err != nil {
   700  			return "", "", err
   701  		}
   702  		yamlConfig, err = r.FetchOCTokenForKubeConfig(yamlConfig, &clusterInfo, clusterInfo.IsStagingSatelliteCluster())
   703  		if err != nil {
   704  			return "", "", err
   705  		}
   706  		err = ioutil.WriteFile(kubeconfigFileName, yamlConfig, 0644) // check about permissions and truncate
   707  		if err != nil {
   708  			return "", "", err
   709  		}
   710  	}
   711  	return kubeconfigFileName, calicoConfig, nil
   712  }
   713  
   714  //StoreConfigDetail ...
   715  func (r *clusters) StoreConfigDetail(name, dir string, admin, createCalicoConfig bool, target ClusterTargetHeader) (string, ClusterKeyInfo, error) {
   716  	clusterkey := ClusterKeyInfo{}
   717  	var calicoConfig string
   718  	if !helpers.FileExists(dir) {
   719  		return "", clusterkey, fmt.Errorf("Path: %q, to download the config doesn't exist", dir)
   720  	}
   721  	rawURL := fmt.Sprintf("/v1/clusters/%s/config", name)
   722  	if admin {
   723  		rawURL += "/admin"
   724  	}
   725  	if createCalicoConfig {
   726  		rawURL += "?createNetworkConfig=true"
   727  	}
   728  	resultDir := ComputeClusterConfigDir(dir, name, admin)
   729  	err := os.MkdirAll(resultDir, 0755)
   730  	if err != nil {
   731  		return "", clusterkey, fmt.Errorf("Error creating directory to download the cluster config")
   732  	}
   733  	downloadPath := filepath.Join(resultDir, "config.zip")
   734  	trace.Logger.Println("Will download the kubeconfig at", downloadPath)
   735  
   736  	var out *os.File
   737  	if out, err = os.Create(downloadPath); err != nil {
   738  		return "", clusterkey, err
   739  	}
   740  	defer out.Close()
   741  	defer helpers.RemoveFile(downloadPath)
   742  	_, err = r.client.Get(rawURL, out, target.ToMap())
   743  	if err != nil {
   744  		return "", clusterkey, err
   745  	}
   746  	trace.Logger.Println("Downloaded the kubeconfig at", downloadPath)
   747  	if err = helpers.Unzip(downloadPath, resultDir); err != nil {
   748  		return "", clusterkey, err
   749  	}
   750  	trace.Logger.Println("Downloaded the kubec", resultDir)
   751  
   752  	unzipConfigPath, err := kubeConfigDir(resultDir)
   753  	if err != nil {
   754  		return "", clusterkey, err
   755  	}
   756  	trace.Logger.Println("Located unzipped directory: ", unzipConfigPath)
   757  	files, _ := ioutil.ReadDir(unzipConfigPath)
   758  	for _, f := range files {
   759  		fileContent, _ := ioutil.ReadFile(unzipConfigPath + "/" + f.Name())
   760  		if f.Name() == "admin-key.pem" {
   761  			clusterkey.AdminKey = string(fileContent)
   762  		}
   763  		if f.Name() == "admin.pem" {
   764  			clusterkey.Admin = string(fileContent)
   765  		}
   766  		if strings.HasPrefix(f.Name(), "ca-") && strings.HasSuffix(f.Name(), ".pem") {
   767  			clusterkey.ClusterCACertificate = string(fileContent)
   768  		}
   769  		old := filepath.Join(unzipConfigPath, f.Name())
   770  		new := filepath.Join(unzipConfigPath, "../", f.Name())
   771  		err := os.Rename(old, new)
   772  		if err != nil {
   773  			return "", clusterkey, fmt.Errorf("Couldn't rename: %q", err)
   774  		}
   775  	}
   776  	err = os.RemoveAll(unzipConfigPath)
   777  	if err != nil {
   778  		return "", clusterkey, err
   779  	}
   780  	// Locate the yaml file and return the new path
   781  	baseDirFiles, err := ioutil.ReadDir(resultDir)
   782  	if err != nil {
   783  		return "", clusterkey, err
   784  	}
   785  
   786  	if createCalicoConfig {
   787  		// Proccess calico golang template file if it exists
   788  		calicoConfig, err = GenerateCalicoConfig(resultDir)
   789  		if err != nil {
   790  			return "", clusterkey, err
   791  		}
   792  	}
   793  	var kubeconfigFileName string
   794  	for _, baseDirFile := range baseDirFiles {
   795  		if strings.Contains(baseDirFile.Name(), ".yml") {
   796  			kubeconfigFileName = fmt.Sprintf("%s/%s", resultDir, baseDirFile.Name())
   797  			break
   798  		}
   799  	}
   800  	if kubeconfigFileName == "" {
   801  		return "", clusterkey, errors.New("Unable to locate kube config in zip archive")
   802  	}
   803  	kubefile, _ := ioutil.ReadFile(kubeconfigFileName)
   804  	var yamlConfig ConfigFile
   805  	err = yaml.Unmarshal(kubefile, &yamlConfig)
   806  	if err != nil {
   807  		fmt.Printf("Error parsing YAML file: %s\n", err)
   808  	}
   809  	if len(yamlConfig.Clusters) != 0 {
   810  		clusterkey.Host = yamlConfig.Clusters[0].Cluster.Server
   811  	}
   812  	if len(yamlConfig.Users) != 0 {
   813  		clusterkey.Token = yamlConfig.Users[0].User.AuthProvider.Config.IDToken
   814  	}
   815  
   816  	// Block to add token for openshift clusters (This can be temporary until iks team handles openshift clusters)
   817  	clusterInfo, err := r.FindWithOutShowResourcesCompatible(name, target)
   818  	if err != nil {
   819  		// Assuming an error means that this is a vpc cluster, and we're returning existing kubeconfig
   820  		// When we add support for vpcs on openshift clusters, we may want revisit this
   821  		clusterkey.FilePath = kubeconfigFileName
   822  		return calicoConfig, clusterkey, nil
   823  	}
   824  
   825  	if clusterInfo.Type == "openshift" {
   826  		trace.Logger.Println("Cluster Type is openshift trying login to get token")
   827  		var yamlConfig []byte
   828  		if yamlConfig, err = ioutil.ReadFile(kubeconfigFileName); err != nil {
   829  			return "", clusterkey, err
   830  		}
   831  		yamlConfig, err = r.FetchOCTokenForKubeConfig(yamlConfig, &clusterInfo, clusterInfo.IsStagingSatelliteCluster())
   832  		if err != nil {
   833  			return "", clusterkey, err
   834  		}
   835  		err = ioutil.WriteFile(kubeconfigFileName, yamlConfig, 0644) // check about permissions and truncate
   836  		if err != nil {
   837  			return "", clusterkey, err
   838  		}
   839  		openshiftyml, _ := ioutil.ReadFile(kubeconfigFileName)
   840  		var openshiftyaml ConfigFileOpenshift
   841  		err = yaml.Unmarshal(openshiftyml, &openshiftyaml)
   842  		if err != nil {
   843  			fmt.Printf("Error parsing YAML file: %s\n", err)
   844  		}
   845  		openshiftusers := openshiftyaml.Users
   846  		for _, usr := range openshiftusers {
   847  			if strings.HasPrefix(usr.Name, "IAM") {
   848  				clusterkey.Token = usr.User.Token
   849  			}
   850  		}
   851  		if len(openshiftyaml.Clusters) != 0 {
   852  			clusterkey.Host = openshiftyaml.Clusters[0].Cluster.Server
   853  		}
   854  		clusterkey.ClusterCACertificate = ""
   855  
   856  	}
   857  	clusterkey.FilePath = kubeconfigFileName
   858  	return calicoConfig, clusterkey, nil
   859  }
   860  
   861  //kubeConfigDir ...
   862  func kubeConfigDir(baseDir string) (string, error) {
   863  	baseDirFiles, err := ioutil.ReadDir(baseDir)
   864  	if err != nil {
   865  		return "", err
   866  	}
   867  
   868  	// Locate the new directory in form "kubeConfigxxx" stored in the base directory
   869  	for _, baseDirFile := range baseDirFiles {
   870  		if baseDirFile.IsDir() && strings.Index(baseDirFile.Name(), "kubeConfig") == 0 {
   871  			return filepath.Join(path.Clean(baseDir), path.Clean(baseDirFile.Name())), nil
   872  		}
   873  	}
   874  
   875  	return "", errors.New("Unable to locate extracted configuration directory")
   876  }
   877  
   878  //GenerateCalicoConfig ...
   879  func GenerateCalicoConfig(desiredConfigPath string) (string, error) {
   880  	// Proccess calico golang template file if it exists
   881  	calicoConfigFile := fmt.Sprintf("%s/%s", desiredConfigPath, "calicoctl.cfg.template")
   882  	newCalicoConfigFile := fmt.Sprintf("%s/%s", desiredConfigPath, "calicoctl.cfg")
   883  	if _, err := os.Stat(calicoConfigFile); !os.IsNotExist(err) {
   884  		tmpl, err := template.ParseFiles(calicoConfigFile)
   885  		if err != nil {
   886  			return "", fmt.Errorf("Unable to parse network config file: %v", err)
   887  		}
   888  
   889  		newCaliFile, err := os.Create(newCalicoConfigFile)
   890  		if err != nil {
   891  			return "", fmt.Errorf("Failed to create network config file: %v", err)
   892  		}
   893  		defer newCaliFile.Close()
   894  
   895  		templateVars := map[string]string{
   896  			"certDir": desiredConfigPath,
   897  		}
   898  		if err := tmpl.Execute(newCaliFile, templateVars); err != nil {
   899  			return "", fmt.Errorf("Failed to execute template: %v", err)
   900  		}
   901  		return newCalicoConfigFile, nil
   902  	}
   903  	// Return an empty file path if the calico config doesn't exist
   904  	return "", nil
   905  }
   906  
   907  //UnsetCredentials ...
   908  func (r *clusters) UnsetCredentials(target ClusterTargetHeader) error {
   909  	rawURL := fmt.Sprintf("/v1/credentials")
   910  	_, err := r.client.Delete(rawURL, target.ToMap())
   911  	return err
   912  }
   913  
   914  //SetCredentials ...
   915  func (r *clusters) SetCredentials(slUsername, slAPIKey string, target ClusterTargetHeader) error {
   916  	slHeader := &ClusterSoftlayerHeader{
   917  		SoftLayerAPIKey:   slAPIKey,
   918  		SoftLayerUsername: slUsername,
   919  	}
   920  	_, err := r.client.Post("/v1/credentials", nil, nil, target.ToMap(), slHeader.ToMap())
   921  	return err
   922  }
   923  
   924  //BindService ...
   925  func (r *clusters) BindService(params ServiceBindRequest, target ClusterTargetHeader) (ServiceBindResponse, error) {
   926  	rawURL := fmt.Sprintf("/v1/clusters/%s/services", params.ClusterNameOrID)
   927  	payLoad := struct {
   928  		ServiceInstanceNameOrID string `json:"serviceInstanceGUID" binding:"required"`
   929  		NamespaceID             string `json:"namespaceID" binding:"required"`
   930  		Role                    string `json:"role"`
   931  		ServiceKeyJSON          string `json:"serviceKeyJSON"`
   932  		ServiceKeyGUID          string `json:"serviceKeyGUID"`
   933  	}{
   934  		ServiceInstanceNameOrID: params.ServiceInstanceNameOrID,
   935  		NamespaceID:             params.NamespaceID,
   936  		Role:                    params.Role,
   937  		ServiceKeyGUID:          params.ServiceKeyGUID,
   938  	}
   939  	var cluster ServiceBindResponse
   940  	_, err := r.client.Post(rawURL, payLoad, &cluster, target.ToMap())
   941  	return cluster, err
   942  }
   943  
   944  //UnBindService ...
   945  func (r *clusters) UnBindService(clusterNameOrID, namespaceID, serviceInstanceGUID string, target ClusterTargetHeader) error {
   946  	rawURL := fmt.Sprintf("/v1/clusters/%s/services/%s/%s", clusterNameOrID, namespaceID, serviceInstanceGUID)
   947  	_, err := r.client.Delete(rawURL, target.ToMap())
   948  	return err
   949  }
   950  
   951  //ComputeClusterConfigDir ...
   952  func ComputeClusterConfigDir(dir, name string, admin bool) string {
   953  	resultDirPrefix := name
   954  	resultDirSuffix := "_k8sconfig"
   955  	if len(name) < 30 {
   956  		//Make it longer for uniqueness
   957  		h := sha256.New()
   958  		h.Write([]byte(name))
   959  		resultDirPrefix = fmt.Sprintf("%x_%s", h.Sum(nil), name)
   960  	}
   961  	if admin {
   962  		resultDirPrefix = fmt.Sprintf("%s_admin", resultDirPrefix)
   963  	}
   964  	resultDir := filepath.Join(dir, fmt.Sprintf("%s%s", path.Clean(resultDirPrefix), path.Clean(resultDirSuffix)))
   965  	return resultDir
   966  }
   967  
   968  //ListServicesBoundToCluster ...
   969  func (r *clusters) ListServicesBoundToCluster(clusterNameOrID, namespace string, target ClusterTargetHeader) (BoundServices, error) {
   970  	var boundServices BoundServices
   971  	var path string
   972  
   973  	if namespace == "" {
   974  		path = fmt.Sprintf("/v1/clusters/%s/services", clusterNameOrID)
   975  
   976  	} else {
   977  		path = fmt.Sprintf("/v1/clusters/%s/services/%s", clusterNameOrID, namespace)
   978  	}
   979  	_, err := r.client.Get(path, &boundServices, target.ToMap())
   980  	if err != nil {
   981  		return boundServices, err
   982  	}
   983  
   984  	return boundServices, err
   985  }
   986  
   987  //FindServiceBoundToCluster...
   988  func (r *clusters) FindServiceBoundToCluster(clusterNameOrID, serviceNameOrId, namespace string, target ClusterTargetHeader) (BoundService, error) {
   989  	var boundService BoundService
   990  	boundServices, err := r.ListServicesBoundToCluster(clusterNameOrID, namespace, target)
   991  	if err != nil {
   992  		return boundService, err
   993  	}
   994  	for _, boundService := range boundServices {
   995  		if strings.Compare(boundService.ServiceName, serviceNameOrId) == 0 || strings.Compare(boundService.ServiceID, serviceNameOrId) == 0 {
   996  			return boundService, nil
   997  		}
   998  	}
   999  
  1000  	return boundService, err
  1001  }
  1002  
  1003  //RefreshAPIServers requests a refresh of a cluster's API server(s)
  1004  func (r *clusters) RefreshAPIServers(clusterNameOrID string, target ClusterTargetHeader) error {
  1005  	params := MasterAPIServer{Action: "refresh"}
  1006  	rawURL := fmt.Sprintf("/v1/clusters/%s/masters", clusterNameOrID)
  1007  	_, err := r.client.Put(rawURL, params, nil, target.ToMap())
  1008  	return err
  1009  }