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:
parent
a045c4923b
commit
cad231fc15
6 changed files with 189 additions and 2 deletions
|
@ -7,6 +7,7 @@ import (
|
|||
"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/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/test"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/version"
|
||||
|
@ -28,6 +29,7 @@ func RootCommand(experimental bool) *cobra.Command {
|
|||
create.Command(),
|
||||
docs.Command(cmd),
|
||||
jp.Command(),
|
||||
migrate.Command(),
|
||||
test.Command(),
|
||||
version.Command(),
|
||||
)
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
func TestRootCommand(t *testing.T) {
|
||||
cmd := RootCommand(false)
|
||||
assert.NotNil(t, cmd)
|
||||
assert.Len(t, cmd.Commands(), 6)
|
||||
assert.Len(t, cmd.Commands(), 7)
|
||||
err := cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ func TestRootCommand(t *testing.T) {
|
|||
func TestRootCommandExperimental(t *testing.T) {
|
||||
cmd := RootCommand(true)
|
||||
assert.NotNil(t, cmd)
|
||||
assert.Len(t, cmd.Commands(), 8)
|
||||
assert.Len(t, cmd.Commands(), 9)
|
||||
err := cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
|
119
cmd/cli/kubectl-kyverno/commands/migrate/command.go
Normal file
119
cmd/cli/kubectl-kyverno/commands/migrate/command.go
Normal 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
|
||||
}
|
15
cmd/cli/kubectl-kyverno/commands/migrate/doc.go
Normal file
15
cmd/cli/kubectl-kyverno/commands/migrate/doc.go
Normal 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`,
|
||||
},
|
||||
}
|
|
@ -46,6 +46,7 @@ kyverno [flags]
|
|||
* [kyverno docs](kyverno_docs.md) - Generates reference documentation.
|
||||
* [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 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 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.
|
||||
|
|
50
docs/user/cli/commands/kyverno_migrate.md
Normal file
50
docs/user/cli/commands/kyverno_migrate.md
Normal 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.
|
||||
|
Loading…
Reference in a new issue