diff --git a/docs/resources/kubectl_manifest.md b/docs/resources/kubectl_manifest.md
index a180509f..35c55c34 100644
--- a/docs/resources/kubectl_manifest.md
+++ b/docs/resources/kubectl_manifest.md
@@ -37,6 +37,7 @@ YAML
> Note: When the kind is a Deployment, this provider will wait for the deployment to be rolled out automatically for you!
### With explicit `wait_for`
+
If `wait_for` is specified, upon applying the resource, provider will wait for **all** conditions to become true before proceeding further.
```hcl
@@ -88,23 +89,21 @@ YAML
* `validate_schema` - Optional. Setting to `false` will mimic `kubectl apply --validate=false` mode. Default `true`.
* `wait` - Optional. Set this flag to wait or not for finalized to complete for deleted objects. Default `false`.
* `wait_for_rollout` - Optional. Set this flag to wait or not for Deployments and APIService to complete rollout. Default `true`.
-* `wait_for`- Optional. If set, will wait until either all conditions are satisfied, or until timeout is reached (see [below for nested schema](#nestedblock--wait_for)). Under the hood [gojsonq](https://github.com/thedevsaddam/gojsonq) is used for querying, see the related syntax and examples
+* `wait_for` - Optional. If set, will wait until either all conditions are satisfied, or until timeout is reached (see [below for nested schema](#wait_for)). Under the hood [gojsonq](https://github.com/thedevsaddam/gojsonq) is used for querying, see the related syntax and examples.
+* `delete_cascade` - Optional; `Background` or `Foreground` are valid options. If set this overrides the default provider behaviour which is to use `Background` unless `wait` is `true` when `Foreground` will be used. To duplicate the default behaviour of `kubectl` this should be explicitly set to `Background`.
-### Nested schemas
-
-### Nested Schema for `wait_for`
+### `wait_for`
Required:
-- `field` (Block List, Min: 1) Condition criteria for a field (see [below for nested schema](#nestedblock--wait_for--field))
+* `field` (Block List, Min: 1) Condition criteria for a field (see [below for nested schema](#wait_forfield))
-
-### Nested Schema for `wait_for.field`
+### `wait_for.field`
Required:
-- `key` (String) Key which should be matched from resulting object
-- `value` (String) Value to wait for
+* `key` (String) Key which should be matched from resulting object
+* `value` (String) Value to wait for
Optional:
@@ -152,7 +151,6 @@ YAML
> Note: Only Map values are supported to be made sensitive. If you need to make a value from a list (or sub-list) sensitive, you can set the high-level key as sensitive to suppress the entire tree output.
-
## Ignore Manifest Fields
You can configure a list of yaml keys to ignore changes to via the `ignore_fields` field.
@@ -219,7 +217,7 @@ You can disable this behavior by setting the `wait_for_rollout` field to `false`
This provider supports importing existing resources. The ID format expected uses a double `//` as a deliminator (as apiVersion can have a forward-slash):
-```
+```shell
# Import the my-namespace Namespace
terraform import kubectl_manifest.my-namespace v1//Namespace//my-namespace
diff --git a/kubernetes/resource_kubectl_manifest.go b/kubernetes/resource_kubectl_manifest.go
index d816f7b0..6af9d2d8 100644
--- a/kubernetes/resource_kubectl_manifest.go
+++ b/kubernetes/resource_kubectl_manifest.go
@@ -5,9 +5,6 @@ import (
"crypto/sha256"
"encoding/base64"
"fmt"
- "github.com/sergi/go-diff/diffmatchpatch"
- "k8s.io/cli-runtime/pkg/genericiooptions"
- k8sdelete "k8s.io/kubectl/pkg/cmd/delete"
"log"
"os"
"regexp"
@@ -15,6 +12,10 @@ import (
"strings"
"time"
+ "github.com/sergi/go-diff/diffmatchpatch"
+ "k8s.io/cli-runtime/pkg/genericiooptions"
+ k8sdelete "k8s.io/kubectl/pkg/cmd/delete"
+
"github.com/alekc/terraform-provider-kubectl/flatten"
"github.com/alekc/terraform-provider-kubectl/internal/types"
@@ -24,7 +25,9 @@ import (
validate2 "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/mitchellh/mapstructure"
"github.com/thedevsaddam/gojsonq/v2"
+ "k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/util/sets"
+ "k8s.io/apimachinery/pkg/watch"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/printers"
"k8s.io/kubectl/pkg/scheme"
@@ -411,7 +414,7 @@ var (
},
"field_manager": {
Type: schema.TypeString,
- Description: "Override the default field manager name. This is only relevent when using server-side apply.",
+ Description: "Override the default field manager name. This is only relevant when using server-side apply.",
Optional: true,
Default: "kubectl",
},
@@ -487,6 +490,12 @@ var (
},
},
},
+ "delete_cascade": {
+ Type: schema.TypeString,
+ Description: "Cascade mode for delete operations, explicitly setting this to Background to match kubectl is recommended. Default is Background unless wait has been set when it will be Foreground.",
+ Optional: true,
+ ValidateDiagFunc: validate2.ToDiagFunc(validate2.StringInSlice([]string{string(meta_v1.DeletePropagationBackground), string(meta_v1.DeletePropagationForeground)}, false)),
+ },
}
)
@@ -719,29 +728,47 @@ func resourceKubectlManifestDelete(ctx context.Context, d *schema.ResourceData,
log.Printf("[INFO] %s perform delete of manifest", manifest)
- propagationPolicy := meta_v1.DeletePropagationBackground
waitForDelete := d.Get("wait").(bool)
- if waitForDelete {
+
+ var propagationPolicy meta_v1.DeletionPropagation
+ cascadeInput := d.Get("delete_cascade").(string)
+ if len(cascadeInput) > 0 {
+ propagationPolicy = meta_v1.DeletionPropagation(cascadeInput)
+ } else if waitForDelete {
propagationPolicy = meta_v1.DeletePropagationForeground
+ } else {
+ propagationPolicy = meta_v1.DeletePropagationBackground
}
+
err = restClient.ResourceInterface.Delete(ctx, manifest.GetName(), meta_v1.DeleteOptions{PropagationPolicy: &propagationPolicy})
resourceGone := errors.IsGone(err) || errors.IsNotFound(err)
if err != nil && !resourceGone {
return fmt.Errorf("%v failed to delete kubernetes resource: %+v", manifest, err)
}
- // at the moment the foreground propagation policy does not behave as expected (it won't block waiting for deletion
- // and it's up to us to check that the object has been successfully deleted.
- for waitForDelete {
- _, err := restClient.ResourceInterface.Get(ctx, manifest.GetName(), meta_v1.GetOptions{})
- resourceGone = errors.IsGone(err) || errors.IsNotFound(err)
+
+ // The rest client doesn't wait for the delete so we need custom logic
+ if waitForDelete {
+ log.Printf("[INFO] %s waiting for delete of manifest to complete", manifest)
+
+ watcher, err := restClient.ResourceInterface.Watch(ctx, meta_v1.ListOptions{FieldSelector: fields.OneTermEqualSelector("metadata.name", manifest.GetName()).String()})
if err != nil {
- if resourceGone {
- break
+ return err
+ }
+
+ defer watcher.Stop()
+
+ deleted := false
+ for !deleted {
+ select {
+ case event := <-watcher.ResultChan():
+ if event.Type == watch.Deleted {
+ deleted = true
+ }
+
+ case <-ctx.Done():
+ return fmt.Errorf("%v failed to delete kubernetes resource: %+v", manifest, err)
}
- return fmt.Errorf("%v failed to delete kubernetes resource: %+v", manifest, err)
}
- log.Printf("[DEBUG] %v waiting for deletion of the resource:\n%s", manifest, yamlBody)
- time.Sleep(time.Second * 10)
}
// Success remove it from state
diff --git a/kubernetes/resource_kubectl_manifest_test.go b/kubernetes/resource_kubectl_manifest_test.go
index faa2e602..d3d4e4b8 100644
--- a/kubernetes/resource_kubectl_manifest_test.go
+++ b/kubernetes/resource_kubectl_manifest_test.go
@@ -3,15 +3,16 @@ package kubernetes
import (
"bytes"
"fmt"
+ "log"
+ "os"
+ "regexp"
+ "testing"
+
"github.com/alekc/terraform-provider-kubectl/yaml"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
- "log"
- "os"
- "regexp"
- "testing"
)
func TestKubectlManifest_RetryOnFailure(t *testing.T) {
@@ -38,6 +39,191 @@ YAML
})
}
+func TestAccKubectl(t *testing.T) {
+ //language=hcl
+ config := `
+resource "kubectl_manifest" "test" {
+ wait = true
+ yaml_body = <