Skip to content

Commit

Permalink
feat: Optimised watch patterns
Browse files Browse the repository at this point in the history
Signed-off-by: Steve Hipwell <[email protected]>
  • Loading branch information
stevehipwell authored and alekc committed Oct 24, 2024
1 parent 5f8c474 commit 0c50865
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 41 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ on:
branches:
- master

concurrency:
group: pr-${{ github.ref }}
cancel-in-progress: true

env:
KUBECONFIG: ${{ github.workspace }}/.kube/config

Expand Down
77 changes: 37 additions & 40 deletions kubernetes/resource_kubectl_manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -668,18 +668,8 @@ func resourceKubectlManifestApply(ctx context.Context, d *schema.ResourceData, m
return fmt.Errorf("at least one of `field` or `condition` must be provided in `wait_for` block")
}

rawResponse, err := restClient.ResourceInterface.List(ctx, meta_v1.ListOptions{FieldSelector: fields.OneTermEqualSelector("metadata.name", manifest.GetName()).String()})
if err != nil {
return err
}

resourceVersion, _, err := unstructured.NestedString(rawResponse.Object, "metadata", "resourceVersion")
if err != nil {
return err
}

log.Printf("[INFO] %v waiting for wait conditions for %vmin", manifest, timeout.Minutes())
err = waitForConditions(ctx, restClient, waitFor.Field, waitFor.Condition, manifest.GetName(), resourceVersion, timeout)
err = waitForConditions(ctx, restClient, waitFor.Field, waitFor.Condition, manifest.GetName(), timeout)
if err != nil {
return err
}
Expand Down Expand Up @@ -784,32 +774,19 @@ func resourceKubectlManifestDelete(ctx context.Context, d *schema.ResourceData,
propagationPolicy = meta_v1.DeletePropagationBackground
}

var resourceVersion string
if wait {
rawResponse, err := restClient.ResourceInterface.List(ctx, meta_v1.ListOptions{FieldSelector: fields.OneTermEqualSelector("metadata.name", manifest.GetName()).String()})
if err != nil {
return err
}

resourceVersion, _, err = unstructured.NestedString(rawResponse.Object, "metadata", "resourceVersion")
if err != nil {
return err
}
}

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)
}

// The rest client doesn't wait for the delete so we need custom logic
if wait {
if wait && !resourceGone {
log.Printf("[INFO] %s waiting for delete of manifest to complete", manifest)

timeout := d.Timeout(schema.TimeoutDelete)

err = waitForDelete(ctx, restClient, manifest.GetName(), resourceVersion, timeout)
err = waitForDelete(ctx, restClient, manifest.GetName(), timeout)
if err != nil {
return err
}
Expand Down Expand Up @@ -961,26 +938,46 @@ func checkAPIResourceIsPresent(available []*meta_v1.APIResourceList, resource me
return nil, false
}

func waitForDelete(ctx context.Context, restClient *RestClientResult, name string, resourceVersion string, timeout time.Duration) error {
func waitForDelete(ctx context.Context, restClient *RestClientResult, name string, timeout time.Duration) error {
timeoutSeconds := int64(timeout.Seconds())

watcher, err := restClient.ResourceInterface.Watch(ctx, meta_v1.ListOptions{Watch: true, TimeoutSeconds: &timeoutSeconds, FieldSelector: fields.OneTermEqualSelector("metadata.name", name).String(), ResourceVersion: resourceVersion})
if err != nil {
rawResponse, err := restClient.ResourceInterface.Get(ctx, name, meta_v1.GetOptions{})
resourceGone := errors.IsGone(err) || errors.IsNotFound(err)
if err != nil && !resourceGone {
return err
}

defer watcher.Stop()
if !resourceGone {
resourceVersion, _, err := unstructured.NestedString(rawResponse.Object, "metadata", "resourceVersion")
if err != nil {
return err
}

deleted := false
for !deleted {
select {
case event := <-watcher.ResultChan():
if event.Type == watch.Deleted {
deleted = true
}
watcher, err := restClient.ResourceInterface.Watch(
ctx,
meta_v1.ListOptions{
Watch: true,
TimeoutSeconds: &timeoutSeconds,
FieldSelector: fields.OneTermEqualSelector("metadata.name", name).String(),
ResourceVersion: resourceVersion,
})
if err != nil {
return err
}

case <-ctx.Done():
return fmt.Errorf("%s failed to delete resource", name)
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("%s failed to delete resource", name)
}
}
}

Expand Down Expand Up @@ -1195,7 +1192,7 @@ func waitForApiService(ctx context.Context, provider *KubeProvider, name string,
return nil
}

func waitForConditions(ctx context.Context, restClient *RestClientResult, waitFields []types.WaitForField, waitConditions []types.WaitForStatusCondition, name string, resourceVersion string, timeout time.Duration) error {
func waitForConditions(ctx context.Context, restClient *RestClientResult, waitFields []types.WaitForField, waitConditions []types.WaitForStatusCondition, name string, timeout time.Duration) error {
timeoutSeconds := int64(timeout.Seconds())

watcher, err := restClient.ResourceInterface.Watch(
Expand Down
2 changes: 1 addition & 1 deletion kubernetes/resource_kubectl_manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ spec:
containers:
- name: busybox
image: busybox
command: ["sleep", "30"]
command: ["sleep", "30"]
YAML
}` //start := time.Now()
// atm the actual error is being hidden by the wait context being deleted. Fix this at some point
Expand Down

0 comments on commit 0c50865

Please sign in to comment.