Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[d8] Add d8 edit options cluster\static\provider configurations #64

Merged
merged 38 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions cmd/platform.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
Copyright 2024 Flant JSC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
platform "github.com/deckhouse/deckhouse-cli/internal/platform/cmd"
)

func init() {
rootCmd.AddCommand(platform.NewCommand())
}
2 changes: 1 addition & 1 deletion internal/backup/cmd/cluster-config/cluster_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
"github.com/deckhouse/deckhouse-cli/internal/backup/configs/storageclasses"
"github.com/deckhouse/deckhouse-cli/internal/backup/configs/tarball"
"github.com/deckhouse/deckhouse-cli/internal/backup/configs/whitelist"
"github.com/deckhouse/deckhouse-cli/internal/backup/utilk8s"
"github.com/deckhouse/deckhouse-cli/internal/utilk8s"
)

var clusterConfigLong = templates.LongDesc(`
Expand Down
2 changes: 1 addition & 1 deletion internal/backup/cmd/etcd/etcd.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import (
"k8s.io/client-go/tools/remotecommand"
"k8s.io/kubectl/pkg/util/templates"

"github.com/deckhouse/deckhouse-cli/internal/backup/utilk8s"
"github.com/deckhouse/deckhouse-cli/internal/utilk8s"
)

var etcdLong = templates.LongDesc(`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
Copyright 2024 Flant JSC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cluster_config

import (
"fmt"

"github.com/spf13/cobra"
"k8s.io/kubectl/pkg/util/templates"

"github.com/deckhouse/deckhouse-cli/internal/platform/cmd/edit/editconfig"
"github.com/deckhouse/deckhouse-cli/internal/platform/cmd/edit/flags"
)

var clusterConfigurationLong = templates.LongDesc(`
Edit cluster-configuration in Kubernetes cluster.

© Flant JSC 2024`)

func NewCommand() *cobra.Command {
clusterConfigurationCmd := &cobra.Command{
Use: "cluster-configuration",
Short: "Edit cluster-configuration.",
Long: clusterConfigurationLong,
SilenceErrors: true,
SilenceUsage: true,
RunE: editClusterConfig,
}
flags.AddFlags(clusterConfigurationCmd.Flags())
return clusterConfigurationCmd
}

func editClusterConfig(cmd *cobra.Command, _ []string) error {
err := edit.BaseEditConfigCMD(cmd, "cluster-configuration", "d8-cluster-configuration", "cluster-configuration.yaml")
if err != nil {
return fmt.Errorf("Error updating secret: %w", err)
}
return err
}
49 changes: 49 additions & 0 deletions internal/platform/cmd/edit/edit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
Copyright 2024 Flant JSC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package edit

import (
"github.com/spf13/cobra"
"k8s.io/kubectl/pkg/util/templates"

"github.com/deckhouse/deckhouse-cli/internal/platform/cmd/edit/flags"
cluster_config "github.com/deckhouse/deckhouse-cli/internal/platform/cmd/edit/cluster-configuration"
provider_config "github.com/deckhouse/deckhouse-cli/internal/platform/cmd/edit/provider-cluster-configuration"
static_config "github.com/deckhouse/deckhouse-cli/internal/platform/cmd/edit/static-cluster-configuration"
)

var editLong = templates.LongDesc(`
Change configuration files in Kubernetes cluster conveniently and safely.

© Flant JSC 2024`)

func NewCommand() *cobra.Command {
editCmd := &cobra.Command{
Use: "edit", Short: "Edit configuration files",
Long: editLong,
}

editCmd.AddCommand(
cluster_config.NewCommand(),
static_config.NewCommand(),
provider_config.NewCommand(),
)

flags.AddFlags(editCmd.Flags())

return editCmd
}
127 changes: 127 additions & 0 deletions internal/platform/cmd/edit/editconfig/editconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package edit

import (
"context"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"os"
"os/exec"

"github.com/spf13/cobra"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"

"github.com/deckhouse/deckhouse-cli/internal/utilk8s"
)

func BaseEditConfigCMD(cmd *cobra.Command, name, secret, dataKey string) error {
editor, err := cmd.Flags().GetString("editor")
if err != nil {
return fmt.Errorf("Failed to get editor from --editor flag: %w", err)
}

kubeconfigPath, err := cmd.Flags().GetString("kubeconfig")
if err != nil {
return fmt.Errorf("Failed to setup Kubernetes client: %w", err)
}

_, kubeCl, err := utilk8s.SetupK8sClientSet(kubeconfigPath)
if err != nil {
return fmt.Errorf("Failed to setup Kubernetes client: %w", err)
}

secretConfig, err := kubeCl.CoreV1().
Secrets("kube-system").
Get(context.Background(), secret, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("Error fetching secret: %w", err)
}

tempFile, err := writeSecretTmp(secretConfig, dataKey)
if err != nil {
return err
}
defer func() {
_ = tempFile.Close()
_ = os.Remove(tempFile.Name())
}()

cmdExec := exec.Command(editor, tempFile.Name())
cmdExec.Stdin = os.Stdin
cmdExec.Stdout = os.Stdout
cmdExec.Stderr = os.Stderr
err = cmdExec.Run()
if err != nil {
return fmt.Errorf("Error opening in editor: %w", err)
}

updatedContent, contentNotChanged, err := compareEditedSecret(tempFile, secretConfig, dataKey)
if err != nil {
return fmt.Errorf("Cannot open edited temp file: %w", err)
}

if contentNotChanged {
return nil
}

encodedValue, err := encodeSecretTmp(updatedContent, dataKey)
_, err = kubeCl.CoreV1().
Secrets("kube-system").
Patch(context.TODO(), secret, types.MergePatchType, encodedValue, metav1.PatchOptions{})
if err != nil {
return fmt.Errorf("Error updating secret: %w", err)
}

fmt.Println("Secret updated successfully")
return err
}

func writeSecretTmp(secretConfig *v1.Secret, dataKey string) (*os.File, error) {
tempFile, err := os.CreateTemp(os.TempDir(), "secret.*.yaml")
if err != nil {
return nil, fmt.Errorf("Can't save cluster configuration: %w\n", err)
}

_, err = tempFile.Write(secretConfig.Data[dataKey])
if err != nil {
return nil, fmt.Errorf("Error writing decoded data to file: %w", err)
}
if err = tempFile.Sync(); err != nil {
return nil, fmt.Errorf("Sync temp file buffers to disk: %w", err)
}

return tempFile, nil
}

func compareEditedSecret(tempFile *os.File, secretConfig *v1.Secret, dataKey string) ([]byte, bool, error) {
updatedContent, err := os.ReadFile(tempFile.Name())
if err != nil {
return nil, false, fmt.Errorf("Error reading updated file: %w", err)
}

if sha256.Sum256(secretConfig.Data[dataKey]) == sha256.Sum256(updatedContent) {
fmt.Println("Configurations are equal. Nothing to update.")
return nil, true, nil
}

return updatedContent, false, nil
}

func encodeSecretTmp(updatedContent []byte, dataKey string) ([]byte, error) {
encodedValue := base64.StdEncoding.EncodeToString(updatedContent)
patchData := map[string]interface{}{
"data": map[string]interface{}{
dataKey: encodedValue,
},
}

patchBytes, err := json.Marshal(patchData)
if err != nil {
return nil, fmt.Errorf("Error convert to json updated data: %w", err)
}

return patchBytes, nil
}
29 changes: 29 additions & 0 deletions internal/platform/cmd/edit/flags/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
Copyright 2024 Flant JSC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package flags

import (
"github.com/spf13/pflag"
)

func AddFlags(flagSet *pflag.FlagSet) {
flagSet.StringP(
"editor", "e",
"vi",
"Your favourite editor.",
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
Copyright 2024 Flant JSC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package provider_config

import (
"fmt"

"github.com/spf13/cobra"
"k8s.io/kubectl/pkg/util/templates"

"github.com/deckhouse/deckhouse-cli/internal/platform/cmd/edit/editconfig"
"github.com/deckhouse/deckhouse-cli/internal/platform/cmd/edit/flags"
)

var providerClusterConfigurationLong = templates.LongDesc(`
Edit provider-cluster-configuration in Kubernetes cluster.

© Flant JSC 2024`)

func NewCommand() *cobra.Command {
providerClusterConfigurationCmd := &cobra.Command{
Use: "provider-cluster-configuration",
Short: "Edit provider-cluster-configuration.",
Long: providerClusterConfigurationLong,
SilenceErrors: true,
SilenceUsage: true,
RunE: editProviderClusterConfig,
}
flags.AddFlags(providerClusterConfigurationCmd.Flags())
return providerClusterConfigurationCmd
}

func editProviderClusterConfig(cmd *cobra.Command, _ []string) error {
err := edit.BaseEditConfigCMD(cmd, "provider-cluster-configuration", "d8-provider-cluster-configuration", "provider-cluster-configuration.yaml")
if err != nil {
return fmt.Errorf("Error updating secret: %w", err)
}
return err
}
Loading