Skip to content
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
23 changes: 23 additions & 0 deletions api/v1alpha1/queue_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,24 @@ const (
QueueDefaultPersistenceNo QueueDefaultPersistence = "no"
)

// QueueAccessEnabled controls whether GET or PUT is allowed on a queue (MQSC GET/PUT).
// +kubebuilder:validation:Enum=enabled;disabled
type QueueAccessEnabled string

const (
QueueAccessEnabledEnabled QueueAccessEnabled = "enabled"
QueueAccessEnabledDisabled QueueAccessEnabled = "disabled"
)

// QueueSpec defines a queue to maintain on a referenced queue manager.
// +kubebuilder:validation:XValidation:rule="self.type != 'alias' || (has(self.attributes) && (('targq' in self.attributes && size(self.attributes['targq']) > 0) || ('target' in self.attributes && size(self.attributes['target']) > 0)))",message="alias queues require attribute targq (or target)"
// +kubebuilder:validation:XValidation:rule="self.type != 'remote' || (has(self.attributes) && (('xmitq' in self.attributes && size(self.attributes['xmitq']) > 0) || ('transmissionqueue' in self.attributes && size(self.attributes['transmissionqueue']) > 0)))",message="remote queues require attribute xmitq (or transmissionqueue)"
// +kubebuilder:validation:XValidation:rule="self.type != 'remote' || (has(self.attributes) && (('rqmname' in self.attributes && size(self.attributes['rqmname']) > 0) || ('remotemanager' in self.attributes && size(self.attributes['remotemanager']) > 0)))",message="remote queues require attribute rqmname (or remotemanager)"
// +kubebuilder:validation:XValidation:rule="!has(self.maxDepth) || !has(self.attributes) || !self.attributes.exists(k, k.lowerAscii() == 'maxdepth')",message="maxDepth field and attributes.maxdepth are mutually exclusive"
// +kubebuilder:validation:XValidation:rule="!has(self.description) || self.description.size() == 0 || !has(self.attributes) || !self.attributes.exists(k, k.lowerAscii() == 'descr')",message="description field and attributes.descr are mutually exclusive"
// +kubebuilder:validation:XValidation:rule="!has(self.defPersistence) || !has(self.attributes) || !self.attributes.exists(k, k.lowerAscii() == 'defpsist')",message="defPersistence field and attributes.defpsist are mutually exclusive"
// +kubebuilder:validation:XValidation:rule="!has(self.get) || !has(self.attributes) || !self.attributes.exists(k, k.lowerAscii() == 'get')",message="get field and attributes.get are mutually exclusive"
// +kubebuilder:validation:XValidation:rule="!has(self.put) || !has(self.attributes) || !self.attributes.exists(k, k.lowerAscii() == 'put')",message="put field and attributes.put are mutually exclusive"
type QueueSpec struct {
// ConnectionRef names a QueueManagerConnection in the same namespace.
// +kubebuilder:validation:Required
Expand Down Expand Up @@ -76,6 +87,18 @@ type QueueSpec struct {
// +optional
DefPersistence QueueDefaultPersistence `json:"defPersistence,omitempty"`

// Get controls whether applications may retrieve messages from the queue (MQSC GET).
// Mutually exclusive with attributes.get; typed field takes precedence when folded
// into the attribute map for mqadmin.
// +optional
Get QueueAccessEnabled `json:"get,omitempty"`

// Put controls whether applications may put messages on the queue (MQSC PUT).
// Mutually exclusive with attributes.put; typed field takes precedence when folded
// into the attribute map for mqadmin.
// +optional
Put QueueAccessEnabled `json:"put,omitempty"`

// Suspend pauses MQ reconciliation for this object. Status shows Synced=False ReasonSuspended.
// +optional
Suspend bool `json:"suspend,omitempty"`
Expand Down
24 changes: 24 additions & 0 deletions charts/mkurator/crds/messaging.mkurator.dev_queues.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ spec:
Mutually exclusive with attributes.descr; typed field takes precedence when folded
into the attribute map for mqadmin.
type: string
get:
description: |-
Get controls whether applications may retrieve messages from the queue (MQSC GET).
Mutually exclusive with attributes.get; typed field takes precedence when folded
into the attribute map for mqadmin.
enum:
- enabled
- disabled
type: string
maxDepth:
description: |-
MaxDepth is the maximum number of messages the queue can hold (MQSC MAXDEPTH).
Expand All @@ -116,6 +125,15 @@ spec:
maximum: 999999999
minimum: 1
type: integer
put:
description: |-
Put controls whether applications may put messages on the queue (MQSC PUT).
Mutually exclusive with attributes.put; typed field takes precedence when folded
into the attribute map for mqadmin.
enum:
- enabled
- disabled
type: string
queueName:
description: QueueName is the IBM MQ object name.
maxLength: 48
Expand Down Expand Up @@ -173,6 +191,12 @@ spec:
- message: defPersistence field and attributes.defpsist are mutually exclusive
rule: '!has(self.defPersistence) || !has(self.attributes) || !self.attributes.exists(k,
k.lowerAscii() == ''defpsist'')'
- message: get field and attributes.get are mutually exclusive
rule: '!has(self.get) || !has(self.attributes) || !self.attributes.exists(k,
k.lowerAscii() == ''get'')'
- message: put field and attributes.put are mutually exclusive
rule: '!has(self.put) || !has(self.attributes) || !self.attributes.exists(k,
k.lowerAscii() == ''put'')'
status:
description: QueueStatus defines the observed state of Queue.
properties:
Expand Down
24 changes: 24 additions & 0 deletions config/crd/bases/messaging.mkurator.dev_queues.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ spec:
Mutually exclusive with attributes.descr; typed field takes precedence when folded
into the attribute map for mqadmin.
type: string
get:
description: |-
Get controls whether applications may retrieve messages from the queue (MQSC GET).
Mutually exclusive with attributes.get; typed field takes precedence when folded
into the attribute map for mqadmin.
enum:
- enabled
- disabled
type: string
maxDepth:
description: |-
MaxDepth is the maximum number of messages the queue can hold (MQSC MAXDEPTH).
Expand All @@ -116,6 +125,15 @@ spec:
maximum: 999999999
minimum: 1
type: integer
put:
description: |-
Put controls whether applications may put messages on the queue (MQSC PUT).
Mutually exclusive with attributes.put; typed field takes precedence when folded
into the attribute map for mqadmin.
enum:
- enabled
- disabled
type: string
queueName:
description: QueueName is the IBM MQ object name.
maxLength: 48
Expand Down Expand Up @@ -173,6 +191,12 @@ spec:
- message: defPersistence field and attributes.defpsist are mutually exclusive
rule: '!has(self.defPersistence) || !has(self.attributes) || !self.attributes.exists(k,
k.lowerAscii() == ''defpsist'')'
- message: get field and attributes.get are mutually exclusive
rule: '!has(self.get) || !has(self.attributes) || !self.attributes.exists(k,
k.lowerAscii() == ''get'')'
- message: put field and attributes.put are mutually exclusive
rule: '!has(self.put) || !has(self.attributes) || !self.attributes.exists(k,
k.lowerAscii() == ''put'')'
status:
description: QueueStatus defines the observed state of Queue.
properties:
Expand Down
2 changes: 0 additions & 2 deletions internal/controller/mq_request_context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
)

func TestSetMQRequestTimeout(t *testing.T) {
t.Parallel()
prev := MQRequestTimeout()
t.Cleanup(func() { SetMQRequestTimeout(prev) })
SetMQRequestTimeout(0)
Expand All @@ -20,7 +19,6 @@ func TestSetMQRequestTimeout(t *testing.T) {
}

func TestMQRequestContextDeadline(t *testing.T) {
t.Parallel()
prev := MQRequestTimeout()
t.Cleanup(func() { SetMQRequestTimeout(prev) })
SetMQRequestTimeout(50 * time.Millisecond)
Expand Down
6 changes: 6 additions & 0 deletions internal/controller/queue_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,12 @@ func toMQQueueSpec(q *messagingv1alpha1.Queue) mqadmin.QueueSpec {
if q.Spec.DefPersistence != "" {
attrs[mqadmin.NormalizeAttrKey("defpsist")] = string(q.Spec.DefPersistence)
}
if q.Spec.Get != "" {
attrs[mqadmin.NormalizeAttrKey("get")] = string(q.Spec.Get)
}
if q.Spec.Put != "" {
attrs[mqadmin.NormalizeAttrKey("put")] = string(q.Spec.Put)
}
return mqadmin.QueueSpec{
Name: q.Spec.QueueName,
Type: mqadmin.QueueType(q.Spec.Type),
Expand Down
19 changes: 19 additions & 0 deletions internal/controller/queue_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,25 @@ func TestToMQQueueSpecTypedDefPersistence(t *testing.T) {
}
}

func TestToMQQueueSpecTypedGetPut(t *testing.T) {
t.Parallel()
q := &messagingv1alpha1.Queue{
Spec: messagingv1alpha1.QueueSpec{
QueueName: "APP.ORDERS",
Type: messagingv1alpha1.QueueTypeLocal,
Get: messagingv1alpha1.QueueAccessEnabledEnabled,
Put: messagingv1alpha1.QueueAccessEnabledDisabled,
},
}
spec := toMQQueueSpec(q)
if spec.Attributes["get"] != "enabled" {
t.Fatalf("get = %q", spec.Attributes["get"])
}
if spec.Attributes["put"] != "disabled" {
t.Fatalf("put = %q", spec.Attributes["put"])
}
}

func TestConnectionReady(t *testing.T) {
t.Parallel()
ready := &messagingv1alpha1.QueueManagerConnection{
Expand Down
32 changes: 32 additions & 0 deletions internal/webhook/v1alpha1/cel_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,38 @@ var _ = Describe("CEL validation parity", func() {
Expect(err.Error()).To(ContainSubstring("defPersistence"))
})

It("rejects Queue with both get and attributes.get", func() {
ctx := context.Background()
err := webhookK8sClient.Create(ctx, &messagingv1alpha1.Queue{
ObjectMeta: metav1.ObjectMeta{Name: "cel-get", Namespace: ns},
Spec: messagingv1alpha1.QueueSpec{
ConnectionRef: messagingv1alpha1.LocalObjectReference{Name: "qm1"},
QueueName: "APP.ORDERS",
Get: messagingv1alpha1.QueueAccessEnabledEnabled,
Attributes: map[string]string{"get": "enabled"},
},
})
Expect(err).To(HaveOccurred())
Expect(apierrors.IsInvalid(err)).To(BeTrue())
Expect(err.Error()).To(ContainSubstring("get"))
})

It("rejects Queue with both put and attributes.put", func() {
ctx := context.Background()
err := webhookK8sClient.Create(ctx, &messagingv1alpha1.Queue{
ObjectMeta: metav1.ObjectMeta{Name: "cel-put", Namespace: ns},
Spec: messagingv1alpha1.QueueSpec{
ConnectionRef: messagingv1alpha1.LocalObjectReference{Name: "qm1"},
QueueName: "APP.ORDERS",
Put: messagingv1alpha1.QueueAccessEnabledDisabled,
Attributes: map[string]string{"put": "disabled"},
},
})
Expect(err).To(HaveOccurred())
Expect(apierrors.IsInvalid(err)).To(BeTrue())
Expect(err.Error()).To(ContainSubstring("put"))
})

It("rejects ChannelAuthRule ADDRESSMAP without address", func() {
ctx := context.Background()
err := webhookK8sClient.Create(ctx, &messagingv1alpha1.ChannelAuthRule{
Expand Down
24 changes: 24 additions & 0 deletions test/schema/golden/queue.spec.openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ properties:
Mutually exclusive with attributes.descr; typed field takes precedence when folded
into the attribute map for mqadmin.
type: string
get:
description: |-
Get controls whether applications may retrieve messages from the queue (MQSC GET).
Mutually exclusive with attributes.get; typed field takes precedence when folded
into the attribute map for mqadmin.
enum:
- enabled
- disabled
type: string
maxDepth:
description: |-
MaxDepth is the maximum number of messages the queue can hold (MQSC MAXDEPTH).
Expand All @@ -56,6 +65,15 @@ properties:
maximum: 999999999
minimum: 1
type: integer
put:
description: |-
Put controls whether applications may put messages on the queue (MQSC PUT).
Mutually exclusive with attributes.put; typed field takes precedence when folded
into the attribute map for mqadmin.
enum:
- enabled
- disabled
type: string
queueName:
description: QueueName is the IBM MQ object name.
maxLength: 48
Expand Down Expand Up @@ -110,3 +128,9 @@ x-kubernetes-validations:
- message: defPersistence field and attributes.defpsist are mutually exclusive
rule: '!has(self.defPersistence) || !has(self.attributes) || !self.attributes.exists(k,
k.lowerAscii() == ''defpsist'')'
- message: get field and attributes.get are mutually exclusive
rule: '!has(self.get) || !has(self.attributes) || !self.attributes.exists(k, k.lowerAscii()
== ''get'')'
- message: put field and attributes.put are mutually exclusive
rule: '!has(self.put) || !has(self.attributes) || !self.attributes.exists(k, k.lowerAscii()
== ''put'')'
Loading