mirror of https://github.com/knative/func.git
feat: delete pipeline and resources with `func delete` (#763)
* feat: delete pipeline and resources with `func delete` Signed-off-by: Zbynek Roubalik <zroubali@redhat.com> * fix test and prompt Signed-off-by: Zbynek Roubalik <zroubali@redhat.com>
This commit is contained in:
parent
6649a71f4d
commit
d478f555cc
38
client.go
38
client.go
|
@ -185,6 +185,7 @@ type DNSProvider interface {
|
||||||
// PipelinesProvider manages lifecyle of CI/CD pipelines used by a Function
|
// PipelinesProvider manages lifecyle of CI/CD pipelines used by a Function
|
||||||
type PipelinesProvider interface {
|
type PipelinesProvider interface {
|
||||||
Run(context.Context, Function) error
|
Run(context.Context, Function) error
|
||||||
|
Remove(context.Context, Function) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// New client for Function management.
|
// New client for Function management.
|
||||||
|
@ -788,17 +789,15 @@ func (c *Client) List(ctx context.Context) ([]ListItem, error) {
|
||||||
|
|
||||||
// Remove a Function. Name takes precidence. If no name is provided,
|
// Remove a Function. Name takes precidence. If no name is provided,
|
||||||
// the Function defined at root is used if it exists.
|
// the Function defined at root is used if it exists.
|
||||||
func (c *Client) Remove(ctx context.Context, cfg Function) error {
|
func (c *Client) Remove(ctx context.Context, cfg Function, deleteAll bool) error {
|
||||||
go func() {
|
go func() {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
c.progressListener.Stopping()
|
c.progressListener.Stopping()
|
||||||
}()
|
}()
|
||||||
// If name is provided, it takes precidence.
|
// If name is provided, it takes precidence.
|
||||||
// Otherwise load the Function deined at root.
|
// Otherwise load the Function defined at root.
|
||||||
if cfg.Name != "" {
|
functionName := cfg.Name
|
||||||
return c.remover.Remove(ctx, cfg.Name)
|
if cfg.Name == "" {
|
||||||
}
|
|
||||||
|
|
||||||
f, err := NewFunction(cfg.Root)
|
f, err := NewFunction(cfg.Root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -806,7 +805,31 @@ func (c *Client) Remove(ctx context.Context, cfg Function) error {
|
||||||
if !f.Initialized() {
|
if !f.Initialized() {
|
||||||
return fmt.Errorf("Function at %v can not be removed unless initialized. Try removing by name", f.Root)
|
return fmt.Errorf("Function at %v can not be removed unless initialized. Try removing by name", f.Root)
|
||||||
}
|
}
|
||||||
return c.remover.Remove(ctx, f.Name)
|
functionName = f.Name
|
||||||
|
cfg = f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete Knative Service and dependent resources in parallel
|
||||||
|
c.progressListener.Increment(fmt.Sprintf("Removing Knative Service: %v", functionName))
|
||||||
|
errChan := make(chan error)
|
||||||
|
go func() {
|
||||||
|
errChan <- c.remover.Remove(ctx, functionName)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var errResources error
|
||||||
|
if deleteAll {
|
||||||
|
c.progressListener.Increment(fmt.Sprintf("Removing Knative Service '%v' and all dependent resources", functionName))
|
||||||
|
errResources = c.pipelinesProvider.Remove(ctx, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
errService := <-errChan
|
||||||
|
|
||||||
|
if errService != nil && errResources != nil {
|
||||||
|
return fmt.Errorf("%s\n%s", errService, errResources)
|
||||||
|
} else if errResources != nil {
|
||||||
|
return errResources
|
||||||
|
}
|
||||||
|
return errService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke is a convenience method for triggering the execution of a Function
|
// Invoke is a convenience method for triggering the execution of a Function
|
||||||
|
@ -918,6 +941,7 @@ func (n *noopDescriber) Describe(context.Context, string) (Instance, error) {
|
||||||
type noopPipelinesProvider struct{}
|
type noopPipelinesProvider struct{}
|
||||||
|
|
||||||
func (n *noopPipelinesProvider) Run(ctx context.Context, _ Function) error { return nil }
|
func (n *noopPipelinesProvider) Run(ctx context.Context, _ Function) error { return nil }
|
||||||
|
func (n *noopPipelinesProvider) Remove(ctx context.Context, _ Function) error { return nil }
|
||||||
|
|
||||||
// DNSProvider
|
// DNSProvider
|
||||||
type noopDNSProvider struct{ output io.Writer }
|
type noopDNSProvider struct{ output io.Writer }
|
||||||
|
|
|
@ -135,7 +135,7 @@ func TestRemove(t *testing.T) {
|
||||||
}
|
}
|
||||||
waitFor(t, client, "remove")
|
waitFor(t, client, "remove")
|
||||||
|
|
||||||
if err := client.Remove(context.Background(), fn.Function{Name: "remove"}); err != nil {
|
if err := client.Remove(context.Background(), fn.Function{Name: "remove"}, false); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +256,7 @@ func newClient(verbose bool) *fn.Client {
|
||||||
func del(t *testing.T, c *fn.Client, name string) {
|
func del(t *testing.T, c *fn.Client, name string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
waitFor(t, c, name)
|
waitFor(t, c, name)
|
||||||
if err := c.Remove(context.Background(), fn.Function{Name: name}); err != nil {
|
if err := c.Remove(context.Background(), fn.Function{Name: name}, false); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -673,7 +673,7 @@ func TestClient_Remove_ByPath(t *testing.T) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := client.Remove(context.Background(), fn.Function{Root: root}); err != nil {
|
if err := client.Remove(context.Background(), fn.Function{Root: root}, false); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -683,6 +683,94 @@ func TestClient_Remove_ByPath(t *testing.T) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestClient_Remove_DeleteAll ensures that the remover is invoked to remove
|
||||||
|
// and that dependent resources are removed as well -> pipeline provider is invoked
|
||||||
|
// the Function with the name of the function at the provided root.
|
||||||
|
func TestClient_Remove_DeleteAll(t *testing.T) {
|
||||||
|
var (
|
||||||
|
root = "testdata/example.com/testRemoveDeleteAll"
|
||||||
|
expectedName = "testRemoveDeleteAll"
|
||||||
|
remover = mock.NewRemover()
|
||||||
|
pipelinesProvider = mock.NewPipelinesProvider()
|
||||||
|
deleteAll = true
|
||||||
|
)
|
||||||
|
|
||||||
|
defer Using(t, root)()
|
||||||
|
|
||||||
|
client := fn.New(
|
||||||
|
fn.WithRegistry(TestRegistry),
|
||||||
|
fn.WithRemover(remover),
|
||||||
|
fn.WithPipelinesProvider(pipelinesProvider))
|
||||||
|
|
||||||
|
if err := client.New(context.Background(), fn.Function{Runtime: TestRuntime, Root: root}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
remover.RemoveFn = func(name string) error {
|
||||||
|
if name != expectedName {
|
||||||
|
t.Fatalf("Expected to remove '%v', got '%v'", expectedName, name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := client.Remove(context.Background(), fn.Function{Root: root}, deleteAll); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !remover.RemoveInvoked {
|
||||||
|
t.Fatal("remover was not invoked")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pipelinesProvider.RemoveInvoked {
|
||||||
|
t.Fatal("pipelinesprovider was not invoked")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestClient_Remove_Dont_DeleteAll ensures that the remover is invoked to remove
|
||||||
|
// and that dependent resources are not removed as well -> pipeline provider not is invoked
|
||||||
|
// the Function with the name of the function at the provided root.
|
||||||
|
func TestClient_Remove_Dont_DeleteAll(t *testing.T) {
|
||||||
|
var (
|
||||||
|
root = "testdata/example.com/testRemoveDontDeleteAll"
|
||||||
|
expectedName = "testRemoveDontDeleteAll"
|
||||||
|
remover = mock.NewRemover()
|
||||||
|
pipelinesProvider = mock.NewPipelinesProvider()
|
||||||
|
deleteAll = false
|
||||||
|
)
|
||||||
|
|
||||||
|
defer Using(t, root)()
|
||||||
|
|
||||||
|
client := fn.New(
|
||||||
|
fn.WithRegistry(TestRegistry),
|
||||||
|
fn.WithRemover(remover),
|
||||||
|
fn.WithPipelinesProvider(pipelinesProvider))
|
||||||
|
|
||||||
|
if err := client.New(context.Background(), fn.Function{Runtime: TestRuntime, Root: root}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
remover.RemoveFn = func(name string) error {
|
||||||
|
if name != expectedName {
|
||||||
|
t.Fatalf("Expected to remove '%v', got '%v'", expectedName, name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := client.Remove(context.Background(), fn.Function{Root: root}, deleteAll); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !remover.RemoveInvoked {
|
||||||
|
t.Fatal("remover was not invoked")
|
||||||
|
}
|
||||||
|
|
||||||
|
if pipelinesProvider.RemoveInvoked {
|
||||||
|
t.Fatal("pipelinesprovider was invoked, but should not")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// TestClient_Remove_ByName ensures that the remover is invoked to remove the function
|
// TestClient_Remove_ByName ensures that the remover is invoked to remove the function
|
||||||
// of the name provided, with precidence over a provided root path.
|
// of the name provided, with precidence over a provided root path.
|
||||||
func TestClient_Remove_ByName(t *testing.T) {
|
func TestClient_Remove_ByName(t *testing.T) {
|
||||||
|
@ -710,12 +798,12 @@ func TestClient_Remove_ByName(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run remove with only a name
|
// Run remove with only a name
|
||||||
if err := client.Remove(context.Background(), fn.Function{Name: expectedName}); err != nil {
|
if err := client.Remove(context.Background(), fn.Function{Name: expectedName}, false); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run remove with a name and a root, which should be ignored in favor of the name.
|
// Run remove with a name and a root, which should be ignored in favor of the name.
|
||||||
if err := client.Remove(context.Background(), fn.Function{Name: expectedName, Root: root}); err != nil {
|
if err := client.Remove(context.Background(), fn.Function{Name: expectedName, Root: root}, false); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -746,7 +834,7 @@ func TestClient_Remove_UninitializedFails(t *testing.T) {
|
||||||
fn.WithRemover(remover))
|
fn.WithRemover(remover))
|
||||||
|
|
||||||
// Attempt to remove by path (uninitialized), expecting an error.
|
// Attempt to remove by path (uninitialized), expecting an error.
|
||||||
if err := client.Remove(context.Background(), fn.Function{Root: root}); err == nil {
|
if err := client.Remove(context.Background(), fn.Function{Root: root}, false); err == nil {
|
||||||
t.Fatalf("did not received expeced error removing an uninitialized func")
|
t.Fatalf("did not received expeced error removing an uninitialized func")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ import (
|
||||||
|
|
||||||
fn "knative.dev/kn-plugin-func"
|
fn "knative.dev/kn-plugin-func"
|
||||||
"knative.dev/kn-plugin-func/knative"
|
"knative.dev/kn-plugin-func/knative"
|
||||||
|
"knative.dev/kn-plugin-func/pipelines/tekton"
|
||||||
|
"knative.dev/kn-plugin-func/progress"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -23,15 +25,26 @@ func init() {
|
||||||
// Testing note: This method is swapped out during testing to allow
|
// Testing note: This method is swapped out during testing to allow
|
||||||
// mocking the remover or the client itself to fabricate test states.
|
// mocking the remover or the client itself to fabricate test states.
|
||||||
func newDeleteClient(cfg deleteConfig) (*fn.Client, error) {
|
func newDeleteClient(cfg deleteConfig) (*fn.Client, error) {
|
||||||
|
listener := progress.New()
|
||||||
remover, err := knative.NewRemover(cfg.Namespace)
|
remover, err := knative.NewRemover(cfg.Namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pipelinesProvider, err := tekton.NewPipelinesProvider(
|
||||||
|
tekton.WithNamespace(cfg.Namespace))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.Verbose = cfg.Verbose
|
||||||
remover.Verbose = cfg.Verbose
|
remover.Verbose = cfg.Verbose
|
||||||
|
pipelinesProvider.Verbose = cfg.Verbose
|
||||||
|
|
||||||
return fn.New(
|
return fn.New(
|
||||||
|
fn.WithProgressListener(listener),
|
||||||
fn.WithRemover(remover),
|
fn.WithRemover(remover),
|
||||||
|
fn.WithPipelinesProvider(pipelinesProvider),
|
||||||
fn.WithVerbose(cfg.Verbose)), nil
|
fn.WithVerbose(cfg.Verbose)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,10 +72,11 @@ kn func delete -n apps myfunc
|
||||||
`,
|
`,
|
||||||
SuggestFor: []string{"remove", "rm", "del"},
|
SuggestFor: []string{"remove", "rm", "del"},
|
||||||
ValidArgsFunction: CompleteFunctionList,
|
ValidArgsFunction: CompleteFunctionList,
|
||||||
PreRunE: bindEnv("path", "confirm", "namespace"),
|
PreRunE: bindEnv("path", "confirm", "namespace", "all"),
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flags().BoolP("confirm", "c", false, "Prompt to confirm all configuration options (Env: $FUNC_CONFIRM)")
|
cmd.Flags().BoolP("confirm", "c", false, "Prompt to confirm all configuration options (Env: $FUNC_CONFIRM)")
|
||||||
|
cmd.Flags().StringP("all", "a", "true", "Delete all resources created for a function, eg. Pipelines, Secrets, etc. (Env: $FUNC_ALL) (allowed values: \"true\", \"false\")")
|
||||||
setNamespaceFlag(cmd)
|
setNamespaceFlag(cmd)
|
||||||
setPathFlag(cmd)
|
setPathFlag(cmd)
|
||||||
|
|
||||||
|
@ -117,13 +131,14 @@ func runDelete(cmd *cobra.Command, args []string, clientFn deleteClientFn) (err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke remove using the concrete client impl
|
// Invoke remove using the concrete client impl
|
||||||
return client.Remove(cmd.Context(), function)
|
return client.Remove(cmd.Context(), function, config.DeleteAll)
|
||||||
}
|
}
|
||||||
|
|
||||||
type deleteConfig struct {
|
type deleteConfig struct {
|
||||||
Name string
|
Name string
|
||||||
Namespace string
|
Namespace string
|
||||||
Path string
|
Path string
|
||||||
|
DeleteAll bool
|
||||||
Verbose bool
|
Verbose bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +152,7 @@ func newDeleteConfig(args []string) deleteConfig {
|
||||||
return deleteConfig{
|
return deleteConfig{
|
||||||
Path: viper.GetString("path"),
|
Path: viper.GetString("path"),
|
||||||
Namespace: viper.GetString("namespace"),
|
Namespace: viper.GetString("namespace"),
|
||||||
|
DeleteAll: viper.GetBool("all"),
|
||||||
Name: deriveName(name, viper.GetString("path")), // args[0] or derived
|
Name: deriveName(name, viper.GetString("path")), // args[0] or derived
|
||||||
Verbose: viper.GetBool("verbose"), // defined on root
|
Verbose: viper.GetBool("verbose"), // defined on root
|
||||||
}
|
}
|
||||||
|
@ -150,11 +166,35 @@ func (c deleteConfig) Prompt() (deleteConfig, error) {
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
dc := deleteConfig{}
|
dc := c
|
||||||
|
var qs = []*survey.Question{
|
||||||
return dc, survey.AskOne(
|
{
|
||||||
&survey.Input{
|
Name: "name",
|
||||||
|
Prompt: &survey.Input{
|
||||||
Message: "Function to remove:",
|
Message: "Function to remove:",
|
||||||
Default: deriveName(c.Name, c.Path)},
|
Default: deriveName(c.Name, c.Path)},
|
||||||
&dc.Name, survey.WithValidator(survey.Required))
|
Validate: survey.Required,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "all",
|
||||||
|
Prompt: &survey.Confirm{
|
||||||
|
Message: "Do you want to delete all resources?",
|
||||||
|
Default: c.DeleteAll,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
answers := struct {
|
||||||
|
Name string
|
||||||
|
All bool
|
||||||
|
}{}
|
||||||
|
|
||||||
|
err := survey.Ask(qs, &answers)
|
||||||
|
if err != nil {
|
||||||
|
return dc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dc.Name = answers.Name
|
||||||
|
dc.DeleteAll = answers.All
|
||||||
|
|
||||||
|
return dc, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,3 +41,12 @@ func CreatePersistentVolumeClaim(ctx context.Context, name, namespaceOverride st
|
||||||
_, err = client.CoreV1().PersistentVolumeClaims(namespace).Create(ctx, pvc, metav1.CreateOptions{})
|
_, err = client.CoreV1().PersistentVolumeClaims(namespace).Create(ctx, pvc, metav1.CreateOptions{})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DeletePersistentVolumeClaims(ctx context.Context, namespaceOverride string, listOptions metav1.ListOptions) (err error) {
|
||||||
|
client, namespace, err := NewClientAndResolvedNamespace(namespaceOverride)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.CoreV1().PersistentVolumeClaims(namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOptions)
|
||||||
|
}
|
||||||
|
|
|
@ -35,3 +35,12 @@ func CreateRoleBindingForServiceAccount(ctx context.Context, name, namespaceOver
|
||||||
_, err = client.RbacV1().RoleBindings(namespace).Create(ctx, rb, metav1.CreateOptions{})
|
_, err = client.RbacV1().RoleBindings(namespace).Create(ctx, rb, metav1.CreateOptions{})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DeleteRoleBindings(ctx context.Context, namespaceOverride string, listOptions metav1.ListOptions) (err error) {
|
||||||
|
client, namespace, err := NewClientAndResolvedNamespace(namespaceOverride)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.RbacV1().RoleBindings(namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOptions)
|
||||||
|
}
|
||||||
|
|
|
@ -36,6 +36,15 @@ func ListSecretsNames(ctx context.Context, namespaceOverride string) (names []st
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DeleteSecrets(ctx context.Context, namespaceOverride string, listOptions metav1.ListOptions) (err error) {
|
||||||
|
client, namespace, err := NewClientAndResolvedNamespace(namespaceOverride)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.CoreV1().Secrets(namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOptions)
|
||||||
|
}
|
||||||
|
|
||||||
func CreateDockerRegistrySecret(ctx context.Context, name, namespaceOverride string, labels map[string]string, username, password, server string) (err error) {
|
func CreateDockerRegistrySecret(ctx context.Context, name, namespaceOverride string, labels map[string]string, username, password, server string) (err error) {
|
||||||
client, namespace, err := NewClientAndResolvedNamespace(namespaceOverride)
|
client, namespace, err := NewClientAndResolvedNamespace(namespaceOverride)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -31,3 +31,12 @@ func CreateServiceAccountWithSecret(ctx context.Context, name, namespaceOverride
|
||||||
_, err = client.CoreV1().ServiceAccounts(namespace).Create(ctx, sa, metav1.CreateOptions{})
|
_, err = client.CoreV1().ServiceAccounts(namespace).Create(ctx, sa, metav1.CreateOptions{})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DeleteServiceAccounts(ctx context.Context, namespaceOverride string, listOptions metav1.ListOptions) (err error) {
|
||||||
|
client, namespace, err := NewClientAndResolvedNamespace(namespaceOverride)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.CoreV1().ServiceAccounts(namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOptions)
|
||||||
|
}
|
||||||
|
|
|
@ -33,8 +33,6 @@ func (remover *Remover) Remove(ctx context.Context, name string) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Removing Knative Service: %v\n", name)
|
|
||||||
|
|
||||||
err = client.DeleteService(ctx, name, RemoveTimeout)
|
err = client.DeleteService(ctx, name, RemoveTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("knative remover failed to delete the service: %v", err)
|
err = fmt.Errorf("knative remover failed to delete the service: %v", err)
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
fn "knative.dev/kn-plugin-func"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PipelinesProvider struct {
|
||||||
|
RunInvoked bool
|
||||||
|
RunFn func(fn.Function) error
|
||||||
|
RemoveInvoked bool
|
||||||
|
RemoveFn func(fn.Function) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPipelinesProvider() *PipelinesProvider {
|
||||||
|
return &PipelinesProvider{
|
||||||
|
RunFn: func(fn.Function) error { return nil },
|
||||||
|
RemoveFn: func(fn.Function) error { return nil },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PipelinesProvider) Run(ctx context.Context, f fn.Function) error {
|
||||||
|
p.RunInvoked = true
|
||||||
|
return p.RunFn(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PipelinesProvider) Remove(ctx context.Context, f fn.Function) error {
|
||||||
|
p.RemoveInvoked = true
|
||||||
|
return p.RemoveFn(f)
|
||||||
|
}
|
|
@ -7,7 +7,6 @@ import (
|
||||||
|
|
||||||
"github.com/google/go-containerregistry/pkg/authn"
|
"github.com/google/go-containerregistry/pkg/authn"
|
||||||
"github.com/google/go-containerregistry/pkg/name"
|
"github.com/google/go-containerregistry/pkg/name"
|
||||||
|
|
||||||
"github.com/tektoncd/cli/pkg/pipelinerun"
|
"github.com/tektoncd/cli/pkg/pipelinerun"
|
||||||
"github.com/tektoncd/cli/pkg/taskrun"
|
"github.com/tektoncd/cli/pkg/taskrun"
|
||||||
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
|
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
|
||||||
|
@ -15,6 +14,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
k8slabels "k8s.io/apimachinery/pkg/labels"
|
||||||
|
|
||||||
fn "knative.dev/kn-plugin-func"
|
fn "knative.dev/kn-plugin-func"
|
||||||
"knative.dev/kn-plugin-func/docker"
|
"knative.dev/kn-plugin-func/docker"
|
||||||
|
@ -184,6 +184,59 @@ func (pp *PipelinesProvider) Run(ctx context.Context, f fn.Function) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pp *PipelinesProvider) Remove(ctx context.Context, f fn.Function) error {
|
||||||
|
|
||||||
|
l := k8slabels.SelectorFromSet(k8slabels.Set(map[string]string{labels.FunctionNameKey: f.Name}))
|
||||||
|
listOptions := metav1.ListOptions{
|
||||||
|
LabelSelector: l.String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// let's try to delete all resources in parallel, so the operation doesn't take long
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
deleteFunctions := []func(context.Context, string, metav1.ListOptions) error{
|
||||||
|
deletePipelines,
|
||||||
|
deletePipelineRuns,
|
||||||
|
k8s.DeleteRoleBindings,
|
||||||
|
k8s.DeleteServiceAccounts,
|
||||||
|
k8s.DeleteSecrets,
|
||||||
|
k8s.DeletePersistentVolumeClaims,
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Add(len(deleteFunctions))
|
||||||
|
errChan := make(chan error, len(deleteFunctions))
|
||||||
|
|
||||||
|
for i := range deleteFunctions {
|
||||||
|
df := deleteFunctions[i]
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
err := df(ctx, pp.namespace, listOptions)
|
||||||
|
if err != nil && !errors.IsNotFound(err) {
|
||||||
|
errChan <- err
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
close(errChan)
|
||||||
|
|
||||||
|
// collect all errors and print them
|
||||||
|
var err error
|
||||||
|
errMsg := ""
|
||||||
|
anyError := false
|
||||||
|
for e := range errChan {
|
||||||
|
if !anyError {
|
||||||
|
anyError = true
|
||||||
|
errMsg = "error deleting resources:"
|
||||||
|
}
|
||||||
|
errMsg += fmt.Sprintf("\n %v", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if anyError {
|
||||||
|
err = fmt.Errorf("%s", errMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// watchPipelineRunProgress watches the progress of the input PipelineRun
|
// watchPipelineRunProgress watches the progress of the input PipelineRun
|
||||||
// and prints detailed description of the currently executed Tekton Task.
|
// and prints detailed description of the currently executed Tekton Task.
|
||||||
func (pp *PipelinesProvider) watchPipelineRunProgress(pr *v1beta1.PipelineRun) error {
|
func (pp *PipelinesProvider) watchPipelineRunProgress(pr *v1beta1.PipelineRun) error {
|
||||||
|
|
|
@ -1,15 +1,35 @@
|
||||||
package tekton
|
package tekton
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
pplnv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
|
pplnv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
fn "knative.dev/kn-plugin-func"
|
fn "knative.dev/kn-plugin-func"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func deletePipelines(ctx context.Context, namespace string, listOptions metav1.ListOptions) (err error) {
|
||||||
|
client, err := NewTektonClient()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.Pipelines(namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deletePipelineRuns(ctx context.Context, namespace string, listOptions metav1.ListOptions) (err error) {
|
||||||
|
client, err := NewTektonClient()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.PipelineRuns(namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOptions)
|
||||||
|
}
|
||||||
|
|
||||||
func generatePipeline(f fn.Function, labels map[string]string) *pplnv1beta1.Pipeline {
|
func generatePipeline(f fn.Function, labels map[string]string) *pplnv1beta1.Pipeline {
|
||||||
pipelineName := getPipelineName(f)
|
pipelineName := getPipelineName(f)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue