diff --git a/cmd/upgrade.go b/cmd/upgrade.go index 654cb3680bfc5661b2fe31ca1c2f8e5124171208..f1d3fb68b137a89fdd7bcbb7b4992c0329c742ad 100755 --- a/cmd/upgrade.go +++ b/cmd/upgrade.go @@ -16,23 +16,17 @@ limitations under the License. package cmd import ( - "context" + "errors" "fmt" "nestos-kubernetes-deployer/cmd/command" "nestos-kubernetes-deployer/cmd/command/opts" "nestos-kubernetes-deployer/pkg/configmanager" "nestos-kubernetes-deployer/pkg/configmanager/asset" "nestos-kubernetes-deployer/pkg/kubeclient" - "time" + "path/filepath" "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "gopkg.in/yaml.v2" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - wait "k8s.io/apimachinery/pkg/util/wait" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func NewUpgradeCommand() *cobra.Command { @@ -47,12 +41,30 @@ func NewUpgradeCommand() *cobra.Command { return upgradeCmd } -func runUpgradeCmd(cmd *cobra.Command, args []string) error { - clusterId, err := cmd.Flags().GetString("cluster-id") +func getFlagString(cmd *cobra.Command, flagName string) string { + flagValue, err := cmd.Flags().GetString(flagName) if err != nil { - logrus.Errorf("Failed to get cluster-id: %v", err) - return err + logrus.Errorf("Failed to get %s parameter: %v", flagName, err) + } + return flagValue +} + +func runUpgradeCmd(cmd *cobra.Command, args []string) error { + clusterId := getFlagString(cmd, "cluster-id") + kubeVersion := getFlagString(cmd, "kube-version") + imageURL := getFlagString(cmd, "imageurl") + if clusterId == "" { + return errors.New("cluster-id is required") + } + + if kubeVersion == "" { + return errors.New("kube-version is required") } + + if imageURL == "" { + return errors.New("imageurl is required") + } + if err := configmanager.Initial(&opts.Opts); err != nil { logrus.Errorf("Failed to initialize configuration parameters: %v", err) return err @@ -71,65 +83,27 @@ func runUpgradeCmd(cmd *cobra.Command, args []string) error { } func upgradeCluster(clusterConfig *asset.ClusterAsset) error { - loopTimeout := 2 * time.Minute - dynamicClient, err := kubeclient.CreateDynamicClient(clusterConfig.AdminKubeConfig) // Define the YAML data for the Custom Resource (CR) yamlData := fmt.Sprintf(` apiVersion: housekeeper.io/v1alpha1 kind: Update metadata: -name: housekeeper-upgrade -namespace: housekeeper-system + name: housekeeper-upgrade + namespace: housekeeper-system spec: -osImageURL: %s -kubeVersion: %s -evictPodForce: %t -maxUnavailable: %d + osImageURL: %s + kubeVersion: %s + evictPodForce: %t + maxUnavailable: %d `, clusterConfig.Housekeeper.OSImageURL, clusterConfig.Housekeeper.KubeVersion, clusterConfig.Housekeeper.EvictPodForce, clusterConfig.Housekeeper.MaxUnavailable) - var unstructuredObj unstructured.Unstructured - err = yaml.Unmarshal([]byte(yamlData), &unstructuredObj) - if err != nil { - logrus.Errorf("Error unmarshalling YAML: %v\n", err) + adminconfig := filepath.Join(configmanager.GetPersistDir(), clusterConfig.Cluster_ID, "admin.config") + if err := kubeclient.DeployCR(yamlData, adminconfig); err != nil { + logrus.Errorf("Failed to deploy Custom Resource: %v", err) return err } - // Create or Update CR - resource := schema.GroupVersionResource{ - Group: "housekeeper.io", - Version: "v1alpha1", - Resource: "updates", // Pluralized resource name - } - - // The loop attempts to create or update a CR until it succeeds or times out - if err := wait.PollImmediate(2*time.Second, loopTimeout, func() (bool, error) { - gvk := unstructuredObj.GroupVersionKind() - dynamicResource := dynamicClient.Resource(gvk.GroupVersion().WithResource(resource.Resource)).Namespace(unstructuredObj.GetNamespace()) - - //Attempts to get the specified Custom Resource from the Kubernetes API Server. - obj, err := dynamicResource.Get(context.Background(), unstructuredObj.GetName(), metav1.GetOptions{}) - if err != nil { - // Not found, create the resource - _, err = dynamicResource.Create(context.Background(), &unstructuredObj, metav1.CreateOptions{}) - if err == nil { - logrus.Infof("Custom Resource created successfully!") - return true, nil - } - } else { - // Found, update the resource - unstructuredObj.SetResourceVersion(obj.GetResourceVersion()) - _, err = dynamicResource.Update(context.Background(), &unstructuredObj, metav1.UpdateOptions{}) - if err == nil { - logrus.Infof("Custom Resource updated successfully!") - return true, nil - } - } - logrus.Errorf("Error creating or updating CR: %v\n", err) - return false, nil - }); err != nil { - logrus.Errorf("Timeout while waiting for Custom Resource to be created or updated.") - } - + logrus.Info("Custom Resource deployed successfully.") return nil } diff --git a/pkg/configmanager/asset/clusterasset.go b/pkg/configmanager/asset/clusterasset.go index 3dc96b3433b68da151c4f8fbe35ffef2f1357f8b..ba4078a8a3f45970b87a807b36df809237338bc6 100644 --- a/pkg/configmanager/asset/clusterasset.go +++ b/pkg/configmanager/asset/clusterasset.go @@ -301,6 +301,10 @@ func (clusterAsset *ClusterAsset) InitClusterAsset(infraAsset InfraAsset, opts * if clusterAsset.Housekeeper.DeployHousekeeper || opts.Housekeeper.DeployHousekeeper { setStringValue(&clusterAsset.Housekeeper.OperatorImageUrl, opts.Housekeeper.OperatorImageUrl, cf.OperatorImageUrl) setStringValue(&clusterAsset.Housekeeper.ControllerImageUrl, opts.Housekeeper.ControllerImageUrl, cf.ControllerImageUrl) + setStringValue(&clusterAsset.Housekeeper.KubeVersion, opts.Housekeeper.KubeVersion, "") + setStringValue(&clusterAsset.Housekeeper.OSImageURL, opts.Housekeeper.OSImageURL, "") + setUIntValue(&clusterAsset.Housekeeper.MaxUnavailable, opts.Housekeeper.MaxUnavailable, cf.MaxUnavailable) + clusterAsset.Housekeeper.EvictPodForce = opts.Housekeeper.EvictPodForce } return clusterAsset, nil @@ -394,6 +398,7 @@ func GetDefaultClusterConfig(arch string) (*ClusterAsset, error) { Housekeeper: Housekeeper{ OperatorImageUrl: OperatorImageUrl, ControllerImageUrl: ControllerImageUrl, + MaxUnavailable: 2, }, }, nil } diff --git a/pkg/kubeclient/ctl.go b/pkg/kubeclient/ctl.go index 83cc758e7213894c483b735d439a4d86819febcd..2c27dfee006d06335df21db8b3cd78e7ca17f6c7 100644 --- a/pkg/kubeclient/ctl.go +++ b/pkg/kubeclient/ctl.go @@ -20,6 +20,7 @@ import ( "github.com/sirupsen/logrus" appsv1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" @@ -77,29 +78,6 @@ func CreateDynamicClient(kubeconfig string) (dynamic.Interface, error) { return dynamicClient, nil } -// Apply a Kubernetes resource of the specified type using the provided content. -// Parameters: -// - clientset: Kubernetes clientset for cluster interaction. -// Input: *kubernetes.Clientset - configured Kubernetes client. -// - resourceType: Type of Kubernetes resource (e.g., "pods", "services"). -// Input: string - type of the Kubernetes resource. -// - content: YAML or JSON content for creating/updating the resource. -// Input: string - content of the Kubernetes resource. -func ApplyResource(clientset *kubernetes.Clientset, resourceType, content string) error { - _, err := clientset.RESTClient(). - Post(). - Resource(resourceType). - Body([]byte(content)). - Do(context.TODO()). - Get() - - if err != nil { - logrus.Errorf("Error applying content: %v", err) - return err - } - return nil -} - func DeployCRD(yamlContent string, kubeconfig string) error { client, err := CreateDynamicClient(kubeconfig) if err != nil { @@ -118,7 +96,7 @@ func DeployCRD(yamlContent string, kubeconfig string) error { apiVersion := "v1" resource := "customresourcedefinitions" - // Create or update the CRD using the dynamic client + // Create the CRD using the dynamic client _, err = client.Resource(schema.GroupVersionResource{ Group: apiGroup, Version: apiVersion, @@ -149,7 +127,7 @@ func DeployNamespace(yamlContent string, kubeconfig string) error { // Specify the API group, version, and resource for Namespaces apiGroup, apiVersion, resource := "", "v1", "namespaces" - // Create or update the Namespace using the dynamic client + // Create the Namespace using the dynamic client _, err = client.Resource(schema.GroupVersionResource{ Group: apiGroup, Version: apiVersion, @@ -240,7 +218,7 @@ func DeployDeployment(yamlContent string, kubeconfig string, namespace string) e return err } - // Create or update the Deployment using the Kubernetes clientset + // Create the Deployment using the Kubernetes clientset _, err = clientset.AppsV1().Deployments(namespace).Create(context.TODO(), deployment, metav1.CreateOptions{}) if err != nil { logrus.Errorf("error creating Deployment: %v", err) @@ -270,7 +248,7 @@ func DeployDaemonSet(yamlContent string, kubeconfig string, namespace string) er return err } - // Create or update the DaemonSet using the Kubernetes clientset + // Create the DaemonSet using the Kubernetes clientset _, err = clientset.AppsV1().DaemonSets(namespace).Create(context.TODO(), daemonSet, metav1.CreateOptions{}) if err != nil { logrus.Errorf("error creating DaemonSet: %v", err) @@ -279,3 +257,73 @@ func DeployDaemonSet(yamlContent string, kubeconfig string, namespace string) er return nil } + +func DeployCR(yamlContent string, kubeconfig string) error { + // Create a dynamic client for interacting with the Kubernetes API server + client, err := CreateDynamicClient(kubeconfig) + if err != nil { + return err + } + + // Parse the YAML content into an Unstructured object + unstructuredObj := &unstructured.Unstructured{} + if err := yaml.Unmarshal([]byte(yamlContent), unstructuredObj); err != nil { + logrus.Errorf("Error parsing YAML as Unstructured: %v", err) + return err + } + + // Specify the API group, version, and resource for the custom resource + apiGroup := "housekeeper.io" + apiVersion := "v1alpha1" + resource := "updates" + + // Try to get the existing custom resource + existingObj, err := client. + Resource(schema.GroupVersionResource{ + Group: apiGroup, + Version: apiVersion, + Resource: resource, + }). + Namespace(unstructuredObj.GetNamespace()). + Get(context.TODO(), unstructuredObj.GetName(), metav1.GetOptions{}) + + if err != nil { + if errors.IsNotFound(err) { + // Custom resource doesn't exist, create it + _, err = client. + Resource(schema.GroupVersionResource{ + Group: apiGroup, + Version: apiVersion, + Resource: resource, + }). + Namespace(unstructuredObj.GetNamespace()). + Create(context.TODO(), unstructuredObj, metav1.CreateOptions{}) + if err != nil { + logrus.Errorf("Error creating custom resource: %v", err) + return err + } + return nil + } + + // Error other than "not found" occurred + logrus.Errorf("Error checking custom resource existence: %v", err) + return err + } + + // Custom resource already exists, update it with new configuration + unstructuredObj.SetResourceVersion(existingObj.GetResourceVersion()) + _, err = client. + Resource(schema.GroupVersionResource{ + Group: apiGroup, + Version: apiVersion, + Resource: resource, + }). + Namespace(unstructuredObj.GetNamespace()). + Update(context.TODO(), unstructuredObj, metav1.UpdateOptions{}) + if err != nil { + logrus.Errorf("Error updating custom resource: %v", err) + return err + } + + return nil +}