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

fix(webhooks): label sync logic in webhooks #1703

Merged
merged 1 commit into from
Mar 28, 2024
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
25 changes: 18 additions & 7 deletions internal/webhook/freight/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"

"github.com/technosophos/moniker"
admissionv1 "k8s.io/api/admission/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand Down Expand Up @@ -86,20 +87,30 @@ func (w *webhook) Default(ctx context.Context, obj runtime.Object) error {
freight.Name = freight.GenerateID()

// Sync the convenience alias field with the alias label
req, err := admission.RequestFromContext(ctx)
if err != nil {
return apierrors.NewInternalError(
fmt.Errorf("error getting admission request from context: %w", err),
)
}
if freight.Labels == nil {
freight.Labels = make(map[string]string, 1)
}
if freight.Alias != "" {
// Alias field has a value, so just copy it to the label
freight.Labels[kargoapi.AliasLabelKey] = freight.Alias
} else if freight.Labels[kargoapi.AliasLabelKey] != "" {
freight.Alias = freight.Labels[kargoapi.AliasLabelKey]
} else {
alias, err := w.getAvailableFreightAliasFn(ctx)
if err != nil {
} else if req.Operation == admissionv1.Create {
// Alias field is empty and this is a create operation, so generate a new
// alias and assign it to both the alias field and the label
var err error
if freight.Alias, err = w.getAvailableFreightAliasFn(ctx); err != nil {
return fmt.Errorf("get available freight alias: %w", err)
}
freight.Alias = alias
freight.Labels[kargoapi.AliasLabelKey] = alias
freight.Labels[kargoapi.AliasLabelKey] = freight.Alias
} else {
// Alias field is empty and this is an update operation, so ensure the
// label does not exist
delete(freight.Labels, kargoapi.AliasLabelKey)
}

return nil
Expand Down
69 changes: 49 additions & 20 deletions internal/webhook/freight/webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import (
"testing"

"github.com/stretchr/testify/require"
admissionv1 "k8s.io/api/admission/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"

kargoapi "github.com/akuity/kargo/api/v1alpha1"
)
Expand All @@ -31,41 +33,41 @@ func TestNewWebhook(t *testing.T) {
func TestDefault(t *testing.T) {
testCases := []struct {
name string
op admissionv1.Operation
webhook *webhook
freight *kargoapi.Freight
assertions func(*testing.T, *kargoapi.Freight, error)
}{
{
name: "sync alias label to alias field",
name: "error getting request from context",
webhook: &webhook{},
freight: &kargoapi.Freight{
Alias: "fake-alias",
},
assertions: func(t *testing.T, freight *kargoapi.Freight, err error) {
require.NoError(t, err)
require.NotEmpty(t, freight.Name)
require.NotEmpty(t, freight.Labels)
require.Equal(t, "fake-alias", freight.Labels[kargoapi.AliasLabelKey])
freight: &kargoapi.Freight{},
assertions: func(t *testing.T, _ *kargoapi.Freight, err error) {
require.Error(t, err)
require.Contains(
t,
err.Error(),
"error getting admission request from context",
)
},
},
{
name: "sync alias field to alias label",
name: "sync alias label to non-empty alias field",
op: admissionv1.Create,
webhook: &webhook{},
freight: &kargoapi.Freight{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
kargoapi.AliasLabelKey: "fake-alias",
},
},
Alias: "fake-alias",
},
assertions: func(t *testing.T, freight *kargoapi.Freight, err error) {
require.NoError(t, err)
require.NotEmpty(t, freight.Name)
require.Equal(t, "fake-alias", freight.Alias)
require.Equal(t, "fake-alias", freight.Labels[kargoapi.AliasLabelKey])
},
},
{
name: "error getting available alias",
op: admissionv1.Create,
webhook: &webhook{
getAvailableFreightAliasFn: func(context.Context) (string, error) {
return "", errors.New("something went wrong")
Expand All @@ -80,6 +82,7 @@ func TestDefault(t *testing.T) {
},
{
name: "success getting available alias",
op: admissionv1.Create,
webhook: &webhook{
getAvailableFreightAliasFn: func(context.Context) (string, error) {
return "fake-alias", nil
Expand All @@ -90,17 +93,43 @@ func TestDefault(t *testing.T) {
require.NoError(t, err)
require.NotEmpty(t, freight.Name)
require.Equal(t, "fake-alias", freight.Alias)
require.NotEmpty(t, freight.Labels)
require.Equal(t, "fake-alias", freight.Labels[kargoapi.AliasLabelKey])
},
},
{
name: "update with empty alias",
op: admissionv1.Update,
webhook: &webhook{},
freight: &kargoapi.Freight{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
kargoapi.AliasLabelKey: "fake-alias",
},
},
},
assertions: func(t *testing.T, freight *kargoapi.Freight, err error) {
require.NoError(t, err)
require.NotEmpty(t, freight.Name)
require.Empty(t, freight.Alias)
_, ok := freight.Labels[kargoapi.AliasLabelKey]
require.False(t, ok)
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
err := testCase.webhook.Default(
context.Background(),
testCase.freight,
ctx := context.Background()
if testCase.op != "" {
ctx = admission.NewContextWithRequest(
ctx,
admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: testCase.op,
},
},
)
}
t.Run(testCase.name, func(t *testing.T) {
err := testCase.webhook.Default(ctx, testCase.freight)
testCase.assertions(t, testCase.freight, err)
})
}
Expand Down
6 changes: 3 additions & 3 deletions internal/webhook/warehouse/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,14 @@ func newWebhook(kubeClient client.Client) *webhook {
func (w *webhook) Default(_ context.Context, obj runtime.Object) error {
warehouse := obj.(*kargoapi.Warehouse) // nolint: forcetypeassert

// Sync the convenience shard field with the shard label
// Sync the shard label to the convenience shard field
if warehouse.Spec.Shard != "" {
if warehouse.Labels == nil {
warehouse.Labels = make(map[string]string, 1)
}
warehouse.Labels[kargoapi.ShardLabelKey] = warehouse.Spec.Shard
} else if warehouse.Labels[kargoapi.ShardLabelKey] != "" {
warehouse.Spec.Shard = warehouse.Labels[kargoapi.ShardLabelKey]
} else {
delete(warehouse.Labels, kargoapi.ShardLabelKey)
}

return nil
Expand Down
15 changes: 7 additions & 8 deletions internal/webhook/warehouse/webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,33 +40,32 @@ func TestDefault(t *testing.T) {
require.Empty(t, warehouse.Spec.Shard)
})

t.Run("sync shard label to shard field", func(t *testing.T) {
t.Run("sync shard label to non-empty shard field", func(t *testing.T) {
warehouse := &kargoapi.Warehouse{
Spec: &kargoapi.WarehouseSpec{
Shard: testShardName,
},
}
err := w.Default(context.Background(), warehouse)
require.NoError(t, err)
require.Equal(t, testShardName, warehouse.Labels[kargoapi.ShardLabelKey])
require.Equal(t, testShardName, warehouse.Spec.Shard)
require.Equal(t, testShardName, warehouse.Labels[kargoapi.ShardLabelKey])
})

t.Run("sync shard field to shard label", func(t *testing.T) {
t.Run("sync shard label to empty shard field", func(t *testing.T) {
warehouse := &kargoapi.Warehouse{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
kargoapi.ShardLabelKey: testShardName,
},
},
Spec: &kargoapi.WarehouseSpec{
Shard: testShardName,
},
Spec: &kargoapi.WarehouseSpec{},
}
err := w.Default(context.Background(), warehouse)
require.NoError(t, err)
require.Equal(t, testShardName, warehouse.Labels[kargoapi.ShardLabelKey])
require.Equal(t, testShardName, warehouse.Spec.Shard)
require.Empty(t, warehouse.Spec.Shard)
_, ok := warehouse.Labels[kargoapi.ShardLabelKey]
require.False(t, ok)
})
}

Expand Down
Loading