1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-09 01:16:55 +00:00
kyverno/cmd/cli/kubectl-kyverno/commands/migrate/command.go
Charles-Edouard Brétéché cad231fc15
feat: add resource migration command (#9296)
* feat: add resource migration command

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* finalize PR

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* fix unit tests

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

---------

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
2024-01-19 11:47:28 +00:00

119 lines
3.7 KiB
Go

package migrate
import (
"context"
"errors"
"fmt"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/command"
"github.com/kyverno/kyverno/pkg/config"
"github.com/spf13/cobra"
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
)
type options struct {
KubeConfig string
Context string
Resources []string
}
func Command() *cobra.Command {
var options options
cmd := &cobra.Command{
Use: "migrate",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
Args: cobra.NoArgs,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, _ []string) error {
clientConfig, err := config.CreateClientConfigWithContext(options.KubeConfig, options.Context)
if err != nil {
return err
}
apiServerClient, err := clientset.NewForConfig(clientConfig)
if err != nil {
return err
}
dynamicClient, err := dynamic.NewForConfig(clientConfig)
if err != nil {
return err
}
for _, resource := range options.Resources {
fmt.Println("migrating resource:", resource, "...")
ctx := context.Background()
crd, err := apiServerClient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, resource, metav1.GetOptions{})
if err != nil {
return err
}
if err := migrate(ctx, crd, dynamicClient, apiServerClient); err != nil {
return err
}
}
return nil
},
}
cmd.Flags().StringVar(&options.KubeConfig, "kubeconfig", "", "path to kubeconfig file with authorization and master location information")
cmd.Flags().StringVar(&options.Context, "context", "", "The name of the kubeconfig context to use")
cmd.Flags().StringSliceVar(&options.Resources, "resource", nil, "The resource to migrate")
return cmd
}
func migrate(ctx context.Context, crd *v1.CustomResourceDefinition, dynamicClient dynamic.Interface, apiServerClient clientset.Interface) error {
var storedVersion *v1.CustomResourceDefinitionVersion
for i := range crd.Spec.Versions {
if crd.Spec.Versions[i].Storage {
storedVersion = &crd.Spec.Versions[i]
}
}
if storedVersion == nil {
return errors.New("stored version not found")
} else {
fmt.Println("stored version:", storedVersion.Name)
if len(crd.Status.StoredVersions) == 1 {
if crd.Status.StoredVersions[0] == storedVersion.Name {
fmt.Println("stored version is already up to date, nothing to do")
return nil
}
}
gvr := schema.GroupVersionResource{
Group: crd.Spec.Group,
Version: storedVersion.Name,
Resource: crd.Spec.Names.Plural,
}
resource := dynamicClient.Resource(gvr)
var client dynamic.ResourceInterface
if crd.Spec.Scope == v1.NamespaceScoped {
client = resource.Namespace(metav1.NamespaceAll)
} else {
client = resource
}
fmt.Println("migrating resources...")
list, err := client.List(ctx, metav1.ListOptions{})
if err != nil {
return err
}
for i := range list.Items {
var client dynamic.ResourceInterface
if crd.Spec.Scope == v1.NamespaceScoped {
client = resource.Namespace(list.Items[i].GetNamespace())
} else {
client = resource
}
_, err := client.Update(ctx, &list.Items[i], metav1.UpdateOptions{})
if err != nil {
return err
}
}
fmt.Println("patching status...")
crd.Status.StoredVersions = []string{storedVersion.Name}
if _, err := apiServerClient.ApiextensionsV1().CustomResourceDefinitions().UpdateStatus(ctx, crd, metav1.UpdateOptions{}); err != nil {
return err
}
}
return nil
}