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 }