Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a Catalog Controllers Lifecycle Integration Test #17435

Merged
merged 2 commits into from
Jun 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions internal/catalog/catalogtest/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,8 @@ func TestControllers_Integration(t *testing.T) {
client := runInMemResourceServiceAndControllers(t, catalog.DefaultControllerDependencies())
RunCatalogV1Alpha1IntegrationTest(t, client)
}

func TestControllers_Lifecycle(t *testing.T) {
client := runInMemResourceServiceAndControllers(t, catalog.DefaultControllerDependencies())
RunCatalogV1Alpha1LifecycleIntegrationTest(t, client)
}
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,7 @@ func expectedGRPCApiServiceEndpoints(t *testing.T, c *rtest.Client) *pbcatalog.S
}

func verifyServiceEndpoints(t *testing.T, c *rtest.Client, id *pbresource.ID, expected *pbcatalog.ServiceEndpoints) {
t.Helper()
c.WaitForResourceState(t, id, func(t rtest.T, res *pbresource.Resource) {
var actual pbcatalog.ServiceEndpoints
err := res.Data.UnmarshalTo(&actual)
Expand Down
706 changes: 706 additions & 0 deletions internal/catalog/catalogtest/test_lifecycle_v1alpha1.go

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions internal/catalog/exports.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ package catalog

import (
"github.com/hashicorp/consul/internal/catalog/internal/controllers"
"github.com/hashicorp/consul/internal/catalog/internal/controllers/endpoints"
"github.com/hashicorp/consul/internal/catalog/internal/controllers/nodehealth"
"github.com/hashicorp/consul/internal/catalog/internal/controllers/workloadhealth"
"github.com/hashicorp/consul/internal/catalog/internal/mappers/nodemapper"
"github.com/hashicorp/consul/internal/catalog/internal/mappers/selectiontracker"
"github.com/hashicorp/consul/internal/catalog/internal/types"
Expand Down Expand Up @@ -40,6 +43,21 @@ var (
HealthStatusV1Alpha1Type = types.HealthStatusV1Alpha1Type
HealthChecksV1Alpha1Type = types.HealthChecksV1Alpha1Type
DNSPolicyV1Alpha1Type = types.DNSPolicyV1Alpha1Type

// Controller Statuses
NodeHealthStatusKey = nodehealth.StatusKey
NodeHealthStatusConditionHealthy = nodehealth.StatusConditionHealthy
NodeHealthConditions = nodehealth.Conditions

WorkloadHealthStatusKey = workloadhealth.StatusKey
WorkloadHealthStatusConditionHealthy = workloadhealth.StatusConditionHealthy
WorkloadHealthConditions = workloadhealth.WorkloadConditions
WorkloadAndNodeHealthConditions = workloadhealth.NodeAndWorkloadConditions

EndpointsStatusKey = endpoints.StatusKey
EndpointsStatusConditionEndpointsManaged = endpoints.StatusConditionEndpointsManaged
EndpointsStatusConditionManaged = endpoints.ConditionManaged
EndpointsStatusConditionUnmanaged = endpoints.ConditionUnmanaged
)

// RegisterTypes adds all resource types within the "catalog" API group
Expand Down
17 changes: 16 additions & 1 deletion internal/resource/resourcetest/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package resourcetest
import (
"context"

"github.com/hashicorp/consul/internal/resource"
"github.com/hashicorp/consul/proto-public/pbresource"
"github.com/oklog/ulid/v2"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/known/anypb"
Expand Down Expand Up @@ -37,6 +40,14 @@ func Resource(rtype *pbresource.Type, name string) *resourceBuilder {
}
}

func ResourceID(id *pbresource.ID) *resourceBuilder {
return &resourceBuilder{
resource: &pbresource.Resource{
Id: id,
},
}
}

func (b *resourceBuilder) WithData(t T, data protoreflect.ProtoMessage) *resourceBuilder {
t.Helper()

Expand Down Expand Up @@ -123,7 +134,11 @@ func (b *resourceBuilder) Write(t T, client pbresource.ResourceServiceClient) *p
_, err := client.Delete(context.Background(), &pbresource.DeleteRequest{
Id: rsp.Resource.Id,
})
require.NoError(t, err)

// ignore not found errors
if err != nil && status.Code(err) != codes.NotFound {
t.Fatalf("Failed to delete resource %s of type %s: %v", rsp.Resource.Id.Name, resource.ToGVK(rsp.Resource.Id.Type), err)
}
})
}

Expand Down
20 changes: 19 additions & 1 deletion internal/resource/resourcetest/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func (client *Client) SetRetryerConfig(timeout time.Duration, wait time.Duration
}

func (client *Client) retry(t T, fn func(r *retry.R)) {
t.Helper()
retryer := &retry.Timer{Timeout: client.timeout, Wait: client.wait}
retry.RunWith(retryer, t, fn)
}
Expand Down Expand Up @@ -181,7 +182,7 @@ func (client *Client) WaitForStatusCondition(t T, id *pbresource.ID, statusKey s

var res *pbresource.Resource
client.retry(t, func(r *retry.R) {
res = client.RequireStatusConditionForCurrentGen(t, id, statusKey, condition)
res = client.RequireStatusConditionForCurrentGen(r, id, statusKey, condition)
})

return res
Expand Down Expand Up @@ -209,10 +210,27 @@ func (client *Client) WaitForResourceState(t T, id *pbresource.ID, verify func(T
return res
}

func (client *Client) WaitForDeletion(t T, id *pbresource.ID) {
t.Helper()

client.retry(t, func(r *retry.R) {
client.RequireResourceNotFound(r, id)
})
}

// ResolveResourceID will read the specified resource and returns its full ID.
// This is mainly useful to get the ID with the Uid filled out.
func (client *Client) ResolveResourceID(t T, id *pbresource.ID) *pbresource.ID {
t.Helper()

return client.RequireResourceExists(t, id).Id
}

func (client *Client) MustDelete(t T, id *pbresource.ID) {
_, err := client.Delete(context.Background(), &pbresource.DeleteRequest{Id: id})
if status.Code(err) == codes.NotFound {
return
}

require.NoError(t, err)
}
2 changes: 1 addition & 1 deletion proto/private/prototest/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,5 @@ func AssertContainsElement[V any](t TestingT, list []V, element V, opts ...cmp.O
}
}

t.Fatalf("assertion failed: list does not contain element\n--- list\n%#v\n--- element: %#v", list, element)
t.Fatalf("assertion failed: list does not contain element\n--- list\n%+v\n--- element: %+v", list, element)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change was needed to avoid triggering the race detector.

The internal/catalog/internal/controllers packages define well known status conditions that the controllers will add to resources. The protobuf system will eventually fill in the unexported size cache bits of the protobuf struct. For some reason the %#v formatting does some copying around of data in a way that copies an atomic value without actually using atomics (reflect.typedmemmove).

Somehow switching to the %+v format specifier avoids that copy and prevents data races.

}