-
Notifications
You must be signed in to change notification settings - Fork 443
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: allow vcluster pro via vcluster create & delete
- Loading branch information
1 parent
a54e5cf
commit 27ef31b
Showing
13 changed files
with
464 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
package create | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strconv" | ||
"time" | ||
|
||
clusterv1 "github.com/loft-sh/agentapi/v3/pkg/apis/loft/cluster/v1" | ||
managementv1 "github.com/loft-sh/api/v3/pkg/apis/management/v1" | ||
storagev1 "github.com/loft-sh/api/v3/pkg/apis/storage/v1" | ||
"github.com/loft-sh/loftctl/v3/cmd/loftctl/cmd/create" | ||
proclient "github.com/loft-sh/loftctl/v3/pkg/client" | ||
"github.com/loft-sh/loftctl/v3/pkg/client/helper" | ||
"github.com/loft-sh/loftctl/v3/pkg/client/naming" | ||
"github.com/loft-sh/loftctl/v3/pkg/config" | ||
"github.com/loft-sh/loftctl/v3/pkg/vcluster" | ||
"github.com/loft-sh/log" | ||
kerrors "k8s.io/apimachinery/pkg/api/errors" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/util/wait" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
) | ||
|
||
func DeployProCluster(ctx context.Context, options *Options, proClient proclient.Client, virtualClusterName string, log log.Logger) error { | ||
// determine project & cluster name | ||
var err error | ||
options.Cluster, options.Project, err = helper.SelectProjectOrCluster(proClient, options.Cluster, options.Project, false, log) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
virtualClusterNamespace := naming.ProjectNamespace(options.Project) | ||
managementClient, err := proClient.Management() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// make sure there is not existing virtual cluster | ||
var virtualClusterInstance *managementv1.VirtualClusterInstance | ||
virtualClusterInstance, err = managementClient.Loft().ManagementV1().VirtualClusterInstances(virtualClusterNamespace).Get(ctx, virtualClusterName, metav1.GetOptions{}) | ||
if err != nil && !kerrors.IsNotFound(err) { | ||
return fmt.Errorf("couldn't retrieve virtual cluster instance: %w", err) | ||
} else if err == nil && !virtualClusterInstance.DeletionTimestamp.IsZero() { | ||
log.Infof("Waiting until virtual cluster is deleted...") | ||
|
||
// wait until the virtual cluster instance is deleted | ||
waitErr := wait.PollUntilContextTimeout(ctx, time.Second, config.Timeout(), false, func(ctx context.Context) (done bool, err error) { | ||
virtualClusterInstance, err = managementClient.Loft().ManagementV1().VirtualClusterInstances(virtualClusterNamespace).Get(ctx, virtualClusterName, metav1.GetOptions{}) | ||
if err != nil && !kerrors.IsNotFound(err) { | ||
return false, err | ||
} else if err == nil && virtualClusterInstance.DeletionTimestamp != nil { | ||
return false, nil | ||
} | ||
|
||
return true, nil | ||
}) | ||
if waitErr != nil { | ||
return fmt.Errorf("get virtual cluster instance: %w", err) | ||
} | ||
|
||
virtualClusterInstance = nil | ||
} else if kerrors.IsNotFound(err) { | ||
virtualClusterInstance = nil | ||
} | ||
|
||
// if the virtual cluster already exists and flag is not set, we terminate | ||
if !options.Upgrade && virtualClusterInstance != nil { | ||
return fmt.Errorf("virtual cluster %s already exists in project %s", virtualClusterName, options.Project) | ||
} | ||
|
||
// create virtual cluster if necessary | ||
if virtualClusterInstance == nil { | ||
// resolve template | ||
virtualClusterTemplate, resolvedParameters, err := create.ResolveTemplate( | ||
proClient, | ||
options.Project, | ||
options.Template, | ||
options.TemplateVersion, | ||
options.SetParams, | ||
options.Params, | ||
log, | ||
) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// create virtual cluster instance | ||
zone, offset := time.Now().Zone() | ||
virtualClusterInstance = &managementv1.VirtualClusterInstance{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Namespace: naming.ProjectNamespace(options.Project), | ||
Name: virtualClusterName, | ||
Annotations: map[string]string{ | ||
clusterv1.SleepModeTimezoneAnnotation: zone + "#" + strconv.Itoa(offset), | ||
}, | ||
}, | ||
Spec: managementv1.VirtualClusterInstanceSpec{ | ||
VirtualClusterInstanceSpec: storagev1.VirtualClusterInstanceSpec{ | ||
TemplateRef: &storagev1.TemplateRef{ | ||
Name: virtualClusterTemplate.Name, | ||
Version: options.TemplateVersion, | ||
}, | ||
ClusterRef: storagev1.VirtualClusterClusterRef{ | ||
ClusterRef: storagev1.ClusterRef{ | ||
Cluster: options.Cluster, | ||
}, | ||
}, | ||
Parameters: resolvedParameters, | ||
}, | ||
}, | ||
} | ||
|
||
// set links | ||
create.SetCustomLinksAnnotation(virtualClusterInstance, options.Links) | ||
|
||
// create virtualclusterinstance | ||
log.Infof("Creating virtual cluster %s in project %s with template %s...", virtualClusterName, options.Project, virtualClusterTemplate.Name) | ||
virtualClusterInstance, err = managementClient.Loft().ManagementV1().VirtualClusterInstances(virtualClusterInstance.Namespace).Create(ctx, virtualClusterInstance, metav1.CreateOptions{}) | ||
if err != nil { | ||
return fmt.Errorf("create virtual cluster: %w", err) | ||
} | ||
} else if options.Upgrade { | ||
// resolve template | ||
virtualClusterTemplate, resolvedParameters, err := create.ResolveTemplate( | ||
proClient, | ||
options.Project, | ||
options.Template, | ||
options.TemplateVersion, | ||
options.SetParams, | ||
options.Params, | ||
log, | ||
) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// update virtual cluster instance | ||
if virtualClusterInstance.Spec.TemplateRef == nil { | ||
return fmt.Errorf("virtual cluster instance doesn't use a template, cannot update virtual cluster") | ||
} | ||
|
||
oldVirtualCluster := virtualClusterInstance.DeepCopy() | ||
templateRefChanged := virtualClusterInstance.Spec.TemplateRef.Name != virtualClusterTemplate.Name | ||
paramsChanged := virtualClusterInstance.Spec.Parameters != resolvedParameters | ||
versionChanged := (options.TemplateVersion != "" && virtualClusterInstance.Spec.TemplateRef.Version != options.TemplateVersion) | ||
linksChanged := create.SetCustomLinksAnnotation(virtualClusterInstance, options.Links) | ||
|
||
// check if update is needed | ||
if templateRefChanged || paramsChanged || versionChanged || linksChanged { | ||
virtualClusterInstance.Spec.TemplateRef.Name = virtualClusterTemplate.Name | ||
virtualClusterInstance.Spec.TemplateRef.Version = options.TemplateVersion | ||
virtualClusterInstance.Spec.Parameters = resolvedParameters | ||
|
||
patch := client.MergeFrom(oldVirtualCluster) | ||
patchData, err := patch.Data(virtualClusterInstance) | ||
if err != nil { | ||
return fmt.Errorf("calculate update patch: %w", err) | ||
} | ||
log.Infof("Updating virtual cluster %s in project %s...", virtualClusterName, options.Project) | ||
virtualClusterInstance, err = managementClient.Loft().ManagementV1().VirtualClusterInstances(virtualClusterInstance.Namespace).Patch(ctx, virtualClusterInstance.Name, patch.Type(), patchData, metav1.PatchOptions{}) | ||
if err != nil { | ||
return fmt.Errorf("patch virtual cluster: %w", err) | ||
} | ||
} else { | ||
log.Infof("Skip updating virtual cluster...") | ||
} | ||
} | ||
|
||
// wait until virtual cluster is ready | ||
virtualClusterInstance, err = vcluster.WaitForVirtualClusterInstance(ctx, managementClient, virtualClusterInstance.Namespace, virtualClusterInstance.Name, true, log) | ||
if err != nil { | ||
return err | ||
} | ||
log.Donef("Successfully created the virtual cluster %s in project %s", virtualClusterName, options.Project) | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.