mirror of https://github.com/knative/pkg.git
Modify cluster manager lib to make it work better with Prow (#755)
* Consolidate cluster manager lib * Adjust unit test * updates based on feedback * Fixing unit tests * Use project as string instead of pointer
This commit is contained in:
parent
9c320664c8
commit
7a2cadb6ad
|
|
@ -52,6 +52,6 @@ func Example() {
|
||||||
if err := gkeOps.Acquire(); err != nil {
|
if err := gkeOps.Acquire(); err != nil {
|
||||||
log.Fatalf("failed acquire cluster: '%v'", err)
|
log.Fatalf("failed acquire cluster: '%v'", err)
|
||||||
}
|
}
|
||||||
log.Printf("GKE project is: %s", *gkeOps.Project)
|
log.Printf("GKE project is: %q", gkeOps.Project)
|
||||||
log.Printf("GKE cluster is: %v", gkeOps.Cluster)
|
log.Printf("GKE cluster is: %v", gkeOps.Cluster)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ const (
|
||||||
regionEnv = "E2E_CLUSTER_REGION"
|
regionEnv = "E2E_CLUSTER_REGION"
|
||||||
backupRegionEnv = "E2E_CLUSTER_BACKUP_REGIONS"
|
backupRegionEnv = "E2E_CLUSTER_BACKUP_REGIONS"
|
||||||
defaultGKEVersion = "latest"
|
defaultGKEVersion = "latest"
|
||||||
|
|
||||||
|
ClusterRunning = "RUNNING"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -70,7 +72,7 @@ type GKERequest struct {
|
||||||
type GKECluster struct {
|
type GKECluster struct {
|
||||||
Request *GKERequest
|
Request *GKERequest
|
||||||
// Project might be GKE specific, so put it here
|
// Project might be GKE specific, so put it here
|
||||||
Project *string
|
Project string
|
||||||
// NeedsCleanup tells whether the cluster needs to be deleted afterwards
|
// NeedsCleanup tells whether the cluster needs to be deleted afterwards
|
||||||
// This probably should be part of task wrapper's logic
|
// This probably should be part of task wrapper's logic
|
||||||
NeedsCleanup bool
|
NeedsCleanup bool
|
||||||
|
|
@ -85,10 +87,18 @@ func (gs *GKEClient) Setup(r GKERequest) ClusterOperations {
|
||||||
gc := &GKECluster{}
|
gc := &GKECluster{}
|
||||||
|
|
||||||
if r.Project != "" { // use provided project and create cluster
|
if r.Project != "" { // use provided project and create cluster
|
||||||
gc.Project = &r.Project
|
gc.Project = r.Project
|
||||||
gc.NeedsCleanup = true
|
gc.NeedsCleanup = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.ClusterName == "" {
|
||||||
|
var err error
|
||||||
|
r.ClusterName, err = getResourceName(ClusterResource)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed getting cluster name: '%v'", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if r.MinNodes == 0 {
|
if r.MinNodes == 0 {
|
||||||
r.MinNodes = DefaultGKEMinNodes
|
r.MinNodes = DefaultGKEMinNodes
|
||||||
}
|
}
|
||||||
|
|
@ -134,41 +144,6 @@ func (gs *GKEClient) Setup(r GKERequest) ClusterOperations {
|
||||||
return gc
|
return gc
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize checks environment for cluster and projects to decide whether using
|
|
||||||
// existing cluster/project or creating new ones.
|
|
||||||
func (gc *GKECluster) initialize() error {
|
|
||||||
// Try obtain project name via `kubectl`, `gcloud`
|
|
||||||
if gc.Project == nil {
|
|
||||||
if err := gc.checkEnvironment(); err != nil {
|
|
||||||
return fmt.Errorf("failed checking existing cluster: '%v'", err)
|
|
||||||
} else if gc.Cluster != nil { // Return if Cluster was already set by kubeconfig
|
|
||||||
// If clustername provided and kubeconfig set, ignore kubeconfig
|
|
||||||
if gc.Request != nil && gc.Request.ClusterName != "" && gc.Cluster.Name != gc.Request.ClusterName {
|
|
||||||
gc.Cluster = nil
|
|
||||||
}
|
|
||||||
if gc.Cluster != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Get project name from boskos if running in Prow
|
|
||||||
if gc.Project == nil && common.IsProw() {
|
|
||||||
project, err := gc.boskosOps.AcquireGKEProject(nil)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed acquiring boskos project: '%v'", err)
|
|
||||||
}
|
|
||||||
gc.Project = &project.Name
|
|
||||||
}
|
|
||||||
if gc.Project == nil || *gc.Project == "" {
|
|
||||||
return errors.New("gcp project must be set")
|
|
||||||
}
|
|
||||||
if !common.IsProw() && gc.Cluster == nil {
|
|
||||||
gc.NeedsCleanup = true
|
|
||||||
}
|
|
||||||
log.Printf("Using project %q for running test", *gc.Project)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Provider returns gke
|
// Provider returns gke
|
||||||
func (gc *GKECluster) Provider() string {
|
func (gc *GKECluster) Provider() string {
|
||||||
return "gke"
|
return "gke"
|
||||||
|
|
@ -179,42 +154,50 @@ func (gc *GKECluster) Provider() string {
|
||||||
// in us-central1, and default BackupRegions are us-west1 and us-east1. If
|
// in us-central1, and default BackupRegions are us-west1 and us-east1. If
|
||||||
// Region or Zone is provided then there is no retries
|
// Region or Zone is provided then there is no retries
|
||||||
func (gc *GKECluster) Acquire() error {
|
func (gc *GKECluster) Acquire() error {
|
||||||
if err := gc.initialize(); err != nil {
|
if err := gc.checkEnvironment(); err != nil {
|
||||||
return fmt.Errorf("failed initializing with environment: '%v'", err)
|
return fmt.Errorf("failed checking project/cluster from environment: '%v'", err)
|
||||||
}
|
}
|
||||||
gc.ensureProtected()
|
// If gc.Cluster is discovered above, then the cluster exists and it's
|
||||||
var err error
|
// project and name matches with requested, use it
|
||||||
// Check if using existing cluster
|
|
||||||
if gc.Cluster != nil {
|
if gc.Cluster != nil {
|
||||||
|
gc.ensureProtected()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if gc.Request.SkipCreation {
|
if gc.Request.SkipCreation {
|
||||||
log.Println("Skipping cluster creation as SkipCreation is set")
|
return errors.New("cannot acquire cluster if SkipCreation is set")
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If comes here we are very likely going to create a cluster, unless
|
||||||
|
// the cluster already exists
|
||||||
|
|
||||||
|
// Cleanup if cluster is created by this client
|
||||||
|
gc.NeedsCleanup = !common.IsProw()
|
||||||
|
|
||||||
|
// Get project name from boskos if running in Prow, otherwise it should fail
|
||||||
|
// since we don't know which project to use
|
||||||
|
if common.IsProw() {
|
||||||
|
project, err := gc.boskosOps.AcquireGKEProject(nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed acquiring boskos project: '%v'", err)
|
||||||
|
}
|
||||||
|
gc.Project = project.Name
|
||||||
|
}
|
||||||
|
if gc.Project == "" {
|
||||||
|
return errors.New("GCP project must be set")
|
||||||
|
}
|
||||||
|
gc.ensureProtected()
|
||||||
|
log.Printf("Identified project %s for cluster creation", gc.Project)
|
||||||
|
|
||||||
// Make a deep copy of the request struct, since the original request is supposed to be immutable
|
// Make a deep copy of the request struct, since the original request is supposed to be immutable
|
||||||
request := gc.Request.DeepCopy()
|
request := gc.Request.DeepCopy()
|
||||||
// Perform GKE specific cluster creation logics
|
// We are going to use request for creating cluster, set its Project
|
||||||
if request.ClusterName == "" {
|
request.Project = gc.Project
|
||||||
request.ClusterName, err = getResourceName(ClusterResource)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed getting cluster name: '%v'", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if request.Project == "" {
|
|
||||||
request.Project = *gc.Project
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Combine Region with BackupRegions, these will be the regions used for
|
||||||
|
// retrying creation logic
|
||||||
regions := []string{request.Region}
|
regions := []string{request.Region}
|
||||||
for _, br := range gc.Request.BackupRegions {
|
for _, br := range gc.Request.BackupRegions {
|
||||||
exist := false
|
if br != request.Region {
|
||||||
for _, region := range regions {
|
|
||||||
if br == region {
|
|
||||||
exist = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !exist {
|
|
||||||
regions = append(regions, br)
|
regions = append(regions, br)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -228,25 +211,23 @@ func (gc *GKECluster) Acquire() error {
|
||||||
err = nil
|
err = nil
|
||||||
|
|
||||||
clusterName := request.ClusterName
|
clusterName := request.ClusterName
|
||||||
// Deleting cluster if it already exists
|
// Use cluster if it already exists and running
|
||||||
existingCluster, _ := gc.operations.GetCluster(*gc.Project, region, request.Zone, clusterName)
|
existingCluster, _ := gc.operations.GetCluster(gc.Project, region, request.Zone, clusterName)
|
||||||
if existingCluster != nil {
|
if existingCluster != nil && existingCluster.Status == ClusterRunning {
|
||||||
log.Printf("Cluster %q already exists in region %q zone %q. Deleting...", clusterName, region, request.Zone)
|
gc.Cluster = existingCluster
|
||||||
err = gc.operations.DeleteCluster(*gc.Project, region, request.Zone, clusterName)
|
return nil
|
||||||
}
|
}
|
||||||
// Creating cluster only if previous step succeeded
|
// Creating cluster
|
||||||
|
log.Printf("Creating cluster %q in region %q zone %q with:\n%+v", clusterName, region, request.Zone, gc.Request)
|
||||||
|
err = gc.operations.CreateCluster(gc.Project, region, request.Zone, rb)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Printf("Creating cluster %q in region %q zone %q with:\n%+v", clusterName, region, request.Zone, gc.Request)
|
cluster, err = gc.operations.GetCluster(gc.Project, region, request.Zone, rb.Cluster.Name)
|
||||||
err = gc.operations.CreateCluster(*gc.Project, region, request.Zone, rb)
|
|
||||||
if err == nil { // Get cluster at last
|
|
||||||
cluster, err = gc.operations.GetCluster(*gc.Project, region, request.Zone, rb.Cluster.Name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errMsg := fmt.Sprintf("Error during cluster creation: '%v'. ", err)
|
errMsg := fmt.Sprintf("Error during cluster creation: '%v'. ", err)
|
||||||
if gc.NeedsCleanup { // Delete half created cluster if it's user created
|
if gc.NeedsCleanup { // Delete half created cluster if it's user created
|
||||||
errMsg = fmt.Sprintf("%sDeleting cluster %q in region %q zone %q in background...\n", errMsg, clusterName, region, request.Zone)
|
errMsg = fmt.Sprintf("%sDeleting cluster %q in region %q zone %q in background...\n", errMsg, clusterName, region, request.Zone)
|
||||||
gc.operations.DeleteClusterAsync(*gc.Project, region, request.Zone, clusterName)
|
gc.operations.DeleteClusterAsync(gc.Project, region, request.Zone, clusterName)
|
||||||
}
|
}
|
||||||
// Retry another region if cluster creation failed.
|
// Retry another region if cluster creation failed.
|
||||||
// TODO(chaodaiG): catch specific errors as we know what the error look like for stockout etc.
|
// TODO(chaodaiG): catch specific errors as we know what the error look like for stockout etc.
|
||||||
|
|
@ -267,15 +248,15 @@ func (gc *GKECluster) Acquire() error {
|
||||||
// Delete takes care of GKE cluster resource cleanup. It only release Boskos resource if running in
|
// Delete takes care of GKE cluster resource cleanup. It only release Boskos resource if running in
|
||||||
// Prow, otherwise deletes the cluster if marked NeedsCleanup
|
// Prow, otherwise deletes the cluster if marked NeedsCleanup
|
||||||
func (gc *GKECluster) Delete() error {
|
func (gc *GKECluster) Delete() error {
|
||||||
if err := gc.initialize(); err != nil {
|
if err := gc.checkEnvironment(); err != nil {
|
||||||
return fmt.Errorf("failed initializing with environment: '%v'", err)
|
return fmt.Errorf("failed checking project/cluster from environment: '%v'", err)
|
||||||
}
|
}
|
||||||
gc.ensureProtected()
|
gc.ensureProtected()
|
||||||
// Release Boskos if running in Prow, will let Janitor taking care of
|
// Release Boskos if running in Prow, will let Janitor taking care of
|
||||||
// clusters deleting
|
// clusters deleting
|
||||||
if common.IsProw() {
|
if common.IsProw() {
|
||||||
log.Printf("Releasing Boskos resource: '%v'", *gc.Project)
|
log.Printf("Releasing Boskos resource: '%v'", gc.Project)
|
||||||
return gc.boskosOps.ReleaseGKEProject(nil, *gc.Project)
|
return gc.boskosOps.ReleaseGKEProject(nil, gc.Project)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NeedsCleanup is only true if running locally and cluster created by the
|
// NeedsCleanup is only true if running locally and cluster created by the
|
||||||
|
|
@ -290,7 +271,7 @@ func (gc *GKECluster) Delete() error {
|
||||||
}
|
}
|
||||||
log.Printf("Deleting cluster %q in %q", gc.Cluster.Name, gc.Cluster.Location)
|
log.Printf("Deleting cluster %q in %q", gc.Cluster.Name, gc.Cluster.Location)
|
||||||
region, zone := gke.RegionZoneFromLoc(gc.Cluster.Location)
|
region, zone := gke.RegionZoneFromLoc(gc.Cluster.Location)
|
||||||
if err := gc.operations.DeleteCluster(*gc.Project, region, zone, gc.Cluster.Name); err != nil {
|
if err := gc.operations.DeleteCluster(gc.Project, region, zone, gc.Cluster.Name); err != nil {
|
||||||
return fmt.Errorf("failed deleting cluster: '%v'", err)
|
return fmt.Errorf("failed deleting cluster: '%v'", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -298,10 +279,10 @@ func (gc *GKECluster) Delete() error {
|
||||||
|
|
||||||
// ensureProtected ensures not operating on protected project/cluster
|
// ensureProtected ensures not operating on protected project/cluster
|
||||||
func (gc *GKECluster) ensureProtected() {
|
func (gc *GKECluster) ensureProtected() {
|
||||||
if gc.Project != nil {
|
if gc.Project != "" {
|
||||||
for _, pp := range protectedProjects {
|
for _, pp := range protectedProjects {
|
||||||
if *gc.Project == pp {
|
if gc.Project == pp {
|
||||||
log.Fatalf("project %q is protected", *gc.Project)
|
log.Fatalf("project %q is protected", gc.Project)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -314,38 +295,56 @@ func (gc *GKECluster) ensureProtected() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// checks for existing cluster by looking at kubeconfig,
|
// checkEnvironment checks environment set for kubeconfig and gcloud, and try to
|
||||||
// and sets up gc.Project and gc.Cluster properly, otherwise fail it.
|
// identify existing project/cluster if they are not set
|
||||||
// if project can be derived from gcloud, sets it up as well
|
//
|
||||||
|
// checks for existing cluster by looking at kubeconfig, if kubeconfig is set:
|
||||||
|
// - If it exists in GKE:
|
||||||
|
// - If Request doesn't contain project/clustername:
|
||||||
|
// - Use it
|
||||||
|
// - If Request contains any of project/clustername:
|
||||||
|
// - If the cluster matches with them:
|
||||||
|
// - Use it
|
||||||
|
// If cluster isn't discovered above, try to get project from gcloud
|
||||||
func (gc *GKECluster) checkEnvironment() error {
|
func (gc *GKECluster) checkEnvironment() error {
|
||||||
var err error
|
|
||||||
// if kubeconfig is configured, use it
|
|
||||||
output, err := common.StandardExec("kubectl", "config", "current-context")
|
output, err := common.StandardExec("kubectl", "config", "current-context")
|
||||||
|
// if kubeconfig is configured, try to use it
|
||||||
if err == nil {
|
if err == nil {
|
||||||
currentContext := strings.TrimSpace(string(output))
|
currentContext := strings.TrimSpace(string(output))
|
||||||
|
log.Printf("kubeconfig is: %q", currentContext)
|
||||||
if strings.HasPrefix(currentContext, "gke_") {
|
if strings.HasPrefix(currentContext, "gke_") {
|
||||||
// output should be in the form of gke_PROJECT_REGION_CLUSTER
|
// output should be in the form of gke_PROJECT_REGION_CLUSTER
|
||||||
parts := strings.Split(currentContext, "_")
|
parts := strings.Split(currentContext, "_")
|
||||||
if len(parts) != 4 { // fall through with warning
|
if len(parts) != 4 { // fall through with warning
|
||||||
log.Printf("WARNING: ignoring kubectl current-context since it's malformed: '%s'", currentContext)
|
log.Printf("WARNING: ignoring kubectl current-context since it's malformed: %q", currentContext)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("kubeconfig isn't empty, uses this cluster for running tests: %s", currentContext)
|
project := parts[1]
|
||||||
gc.Project = &parts[1]
|
|
||||||
location, clusterName := parts[2], parts[3]
|
location, clusterName := parts[2], parts[3]
|
||||||
region, zone := gke.RegionZoneFromLoc(location)
|
region, zone := gke.RegionZoneFromLoc(location)
|
||||||
gc.Cluster, err = gc.operations.GetCluster(*gc.Project, region, zone, clusterName)
|
// Use the cluster only if project and clustername match
|
||||||
if err != nil {
|
if (gc.Request.Project == "" || gc.Request.Project == project) && (gc.Request.ClusterName == "" || gc.Request.ClusterName == clusterName) {
|
||||||
return fmt.Errorf("couldn't find cluster %s in %s in %s, does it exist? %v", clusterName, *gc.Project, location, err)
|
cluster, err := gc.operations.GetCluster(project, region, zone, clusterName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("couldn't find cluster %s in %s in %s, does it exist? %v", clusterName, project, location, err)
|
||||||
|
}
|
||||||
|
gc.Cluster = cluster
|
||||||
|
gc.Project = project
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// When kubeconfig isn't set, the err isn't nil and output should be empty.
|
||||||
|
// If output isn't empty then this is unexpected error, should shout out
|
||||||
|
// directly
|
||||||
if err != nil && len(output) > 0 {
|
if err != nil && len(output) > 0 {
|
||||||
// this is unexpected error, should shout out directly
|
|
||||||
return fmt.Errorf("failed running kubectl config current-context: '%s'", string(output))
|
return fmt.Errorf("failed running kubectl config current-context: '%s'", string(output))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if gc.Project != "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// if gcloud is pointing to a project, use it
|
// if gcloud is pointing to a project, use it
|
||||||
output, err = common.StandardExec("gcloud", "config", "get-value", "project")
|
output, err = common.StandardExec("gcloud", "config", "get-value", "project")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -353,8 +352,7 @@ func (gc *GKECluster) checkEnvironment() error {
|
||||||
}
|
}
|
||||||
if string(output) != "" {
|
if string(output) != "" {
|
||||||
project := strings.Trim(strings.TrimSpace(string(output)), "\n\r")
|
project := strings.Trim(strings.TrimSpace(string(output)), "\n\r")
|
||||||
gc.Project = &project
|
gc.Project = project
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -107,11 +107,7 @@ func (fgsc *GKESDKClient) CreateClusterAsync(
|
||||||
Location: location,
|
Location: location,
|
||||||
Status: "RUNNING",
|
Status: "RUNNING",
|
||||||
AddonsConfig: rb.Cluster.AddonsConfig,
|
AddonsConfig: rb.Cluster.AddonsConfig,
|
||||||
NodePools: []*container.NodePool{
|
NodePools: rb.Cluster.NodePools,
|
||||||
{
|
|
||||||
Name: "default-pool",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
if rb.Cluster.NodePools != nil {
|
if rb.Cluster.NodePools != nil {
|
||||||
cluster.NodePools = rb.Cluster.NodePools
|
cluster.NodePools = rb.Cluster.NodePools
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue