1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

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>
This commit is contained in:
Charles-Edouard Brétéché 2024-01-19 12:47:28 +01:00 committed by GitHub
parent a045c4923b
commit cad231fc15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 189 additions and 2 deletions

View file

@ -7,6 +7,7 @@ import (
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/docs" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/docs"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/fix" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/fix"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/jp" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/jp"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/migrate"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/oci" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/oci"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/test" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/test"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/version" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/version"
@ -28,6 +29,7 @@ func RootCommand(experimental bool) *cobra.Command {
create.Command(), create.Command(),
docs.Command(cmd), docs.Command(cmd),
jp.Command(), jp.Command(),
migrate.Command(),
test.Command(), test.Command(),
version.Command(), version.Command(),
) )

View file

@ -12,7 +12,7 @@ import (
func TestRootCommand(t *testing.T) { func TestRootCommand(t *testing.T) {
cmd := RootCommand(false) cmd := RootCommand(false)
assert.NotNil(t, cmd) assert.NotNil(t, cmd)
assert.Len(t, cmd.Commands(), 6) assert.Len(t, cmd.Commands(), 7)
err := cmd.Execute() err := cmd.Execute()
assert.NoError(t, err) assert.NoError(t, err)
} }
@ -20,7 +20,7 @@ func TestRootCommand(t *testing.T) {
func TestRootCommandExperimental(t *testing.T) { func TestRootCommandExperimental(t *testing.T) {
cmd := RootCommand(true) cmd := RootCommand(true)
assert.NotNil(t, cmd) assert.NotNil(t, cmd)
assert.Len(t, cmd.Commands(), 8) assert.Len(t, cmd.Commands(), 9)
err := cmd.Execute() err := cmd.Execute()
assert.NoError(t, err) assert.NoError(t, err)
} }

View file

@ -0,0 +1,119 @@
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
}

View file

@ -0,0 +1,15 @@
package migrate
// TODO
var websiteUrl = ``
var description = []string{
`Migrate one or more resources to the stored version.`,
}
var examples = [][]string{
{
`# Migrate policy exceptions`,
`kyverno migrate --resource policyexceptions.kyverno.io`,
},
}

View file

@ -46,6 +46,7 @@ kyverno [flags]
* [kyverno docs](kyverno_docs.md) - Generates reference documentation. * [kyverno docs](kyverno_docs.md) - Generates reference documentation.
* [kyverno fix](kyverno_fix.md) - Fix inconsistencies and deprecated usage of Kyverno resources. * [kyverno fix](kyverno_fix.md) - Fix inconsistencies and deprecated usage of Kyverno resources.
* [kyverno jp](kyverno_jp.md) - Provides a command-line interface to JMESPath, enhanced with Kyverno specific custom functions. * [kyverno jp](kyverno_jp.md) - Provides a command-line interface to JMESPath, enhanced with Kyverno specific custom functions.
* [kyverno migrate](kyverno_migrate.md) - Migrate one or more resources to the stored version.
* [kyverno oci](kyverno_oci.md) - Pulls/pushes images that include policie(s) from/to OCI registries. * [kyverno oci](kyverno_oci.md) - Pulls/pushes images that include policie(s) from/to OCI registries.
* [kyverno test](kyverno_test.md) - Run tests from a local filesystem or a remote git repository. * [kyverno test](kyverno_test.md) - Run tests from a local filesystem or a remote git repository.
* [kyverno version](kyverno_version.md) - Prints the version of Kyverno CLI. * [kyverno version](kyverno_version.md) - Prints the version of Kyverno CLI.

View file

@ -0,0 +1,50 @@
## kyverno migrate
Migrate one or more resources to the stored version.
### Synopsis
Migrate one or more resources to the stored version.
```
kyverno migrate [flags]
```
### Examples
```
# Migrate policy exceptions
kyverno migrate --resource policyexceptions.kyverno.io
```
### Options
```
--context string The name of the kubeconfig context to use
-h, --help help for migrate
--kubeconfig string path to kubeconfig file with authorization and master location information
--resource strings The resource to migrate
```
### Options inherited from parent commands
```
--add_dir_header If true, adds the file directory to the header of the log messages
--alsologtostderr log to standard error as well as files (no effect when -logtostderr=true)
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0)
--log_dir string If non-empty, write log files in this directory (no effect when -logtostderr=true)
--log_file string If non-empty, use this log file (no effect when -logtostderr=true)
--log_file_max_size uint Defines the maximum size a log file can grow to (no effect when -logtostderr=true). Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800)
--logtostderr log to standard error instead of files (default true)
--one_output If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true)
--skip_headers If true, avoid header prefixes in the log messages
--skip_log_headers If true, avoid headers when opening log files (no effect when -logtostderr=true)
--stderrthreshold severity logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=true) (default 2)
-v, --v Level number for the log level verbosity
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
```
### SEE ALSO
* [kyverno](kyverno.md) - Kubernetes Native Policy Management.