Skip to content

Commit 43e28be

Browse files
authored
feat: graduate Notes API to dedicated notes.miloapis.com group (#488)
- Create new notes.miloapis.com API group with Note (namespaced) and ClusterNote (cluster-scoped) - Implement controllers with PolicyBinding support for both resource types - Add validation and mutation webhooks for Note and ClusterNote - Generate CRDs for notes.miloapis.com/v1alpha1 - Configure ProtectedResources for IAM integration - Add domain note examples demonstrating all use cases - Remove Notes from crm.miloapis.com (graduated to dedicated group) - Clean up CRM API group structure for future CRM-specific resources - Update all kustomizations and configurations This allows Notes to be attached to any resource (including networking.datumapis.com/Domain) while keeping CRM API group available for future CRM-specific resources like Leads, Opportunities, and Accounts. Needed for datum-cloud/enhancements#501
2 parents 37c317a + 6933e69 commit 43e28be

43 files changed

Lines changed: 2715 additions & 2111 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cmd/milo/controller-manager/controllermanager.go

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,21 +78,22 @@ import (
7878
_ "k8s.io/component-base/logs/json/register"
7979

8080
controlplane "go.miloapis.com/milo/internal/control-plane"
81-
crmcontroller "go.miloapis.com/milo/internal/controllers/crm"
8281
iamcontroller "go.miloapis.com/milo/internal/controllers/iam"
82+
notescontroller "go.miloapis.com/milo/internal/controllers/notes"
8383
remoteapiservicecontroller "go.miloapis.com/milo/internal/controllers/remoteapiservice"
8484
resourcemanagercontroller "go.miloapis.com/milo/internal/controllers/resourcemanager"
8585
infracluster "go.miloapis.com/milo/internal/infra-cluster"
8686
quotacontroller "go.miloapis.com/milo/internal/quota/controllers"
87-
crmv1alpha1webhook "go.miloapis.com/milo/internal/webhooks/crm/v1alpha1"
8887
iamv1alpha1webhook "go.miloapis.com/milo/internal/webhooks/iam/v1alpha1"
8988
identityv1alpha1webhook "go.miloapis.com/milo/internal/webhooks/identity/v1alpha1"
89+
notesv1alpha1webhook "go.miloapis.com/milo/internal/webhooks/notes/v1alpha1"
9090
notificationv1alpha1webhook "go.miloapis.com/milo/internal/webhooks/notification/v1alpha1"
9191
resourcemanagerv1alpha1webhook "go.miloapis.com/milo/internal/webhooks/resourcemanager/v1alpha1"
9292
crmv1alpha1 "go.miloapis.com/milo/pkg/apis/crm/v1alpha1"
9393
iamv1alpha1 "go.miloapis.com/milo/pkg/apis/iam/v1alpha1"
9494
identityv1alpha1 "go.miloapis.com/milo/pkg/apis/identity/v1alpha1"
9595
infrastructurev1alpha1 "go.miloapis.com/milo/pkg/apis/infrastructure/v1alpha1"
96+
notesv1alpha1 "go.miloapis.com/milo/pkg/apis/notes/v1alpha1"
9697
notificationv1alpha1 "go.miloapis.com/milo/pkg/apis/notification/v1alpha1"
9798
quotav1alpha1 "go.miloapis.com/milo/pkg/apis/quota/v1alpha1"
9899
resourcemanagerv1alpha1 "go.miloapis.com/milo/pkg/apis/resourcemanager/v1alpha1"
@@ -176,6 +177,7 @@ func init() {
176177
utilruntime.Must(infrastructurev1alpha1.AddToScheme(Scheme))
177178
utilruntime.Must(iamv1alpha1.AddToScheme(Scheme))
178179
utilruntime.Must(identityv1alpha1.AddToScheme(Scheme))
180+
utilruntime.Must(notesv1alpha1.AddToScheme(Scheme))
179181
utilruntime.Must(notificationv1alpha1.AddToScheme(Scheme))
180182
utilruntime.Must(crmv1alpha1.AddToScheme(Scheme))
181183
utilruntime.Must(quotav1alpha1.AddToScheme(Scheme))
@@ -297,7 +299,7 @@ func NewCommand() *cobra.Command {
297299
fs.StringVar(&PlatformInvitationEmailVariableActionUrl, "platform-invitation-email-variable-action-url", "https://cloud.datum.net", "The action url for the platform invitation email.")
298300
fs.StringVar(&OrganizationMembershipSelfDeleteRoleName, "organization-membership-self-delete-role-name", "organizationmembership-self-delete", "The name of the role that will be used to grant organization membership self delete actions.")
299301
fs.StringVar(&OrganizationMembershipSelfDeleteRoleNamespace, "organization-membership-self-delete-role-namespace", "milo-system", "The namespace where the organization membership self delete role is located. Defaults to system-namespace if not specified.")
300-
fs.StringVar(&NoteCreatorEditorRoleName, "note-creator-editor-role-name", "crm-note-creator-editor", "The name of the role that will be used to grant note creator edit permissions.")
302+
fs.StringVar(&NoteCreatorEditorRoleName, "note-creator-editor-role-name", "notes-creator-editor", "The name of the role that will be used to grant note creator edit permissions.")
301303

302304
fs.IntVar(&s.ControllerRuntimeWebhookPort, "controller-runtime-webhook-port", 9443, "The port to use for the controller-runtime webhook server.")
303305

@@ -546,10 +548,14 @@ func Run(ctx context.Context, c *config.CompletedConfig, opts *Options) error {
546548
logger.Error(err, "Error setting up platform access rejection webhook")
547549
klog.FlushAndExit(klog.ExitFlushTimeout, 1)
548550
}
549-
if err := crmv1alpha1webhook.SetupNoteWebhooksWithManager(ctrl); err != nil {
551+
if err := notesv1alpha1webhook.SetupNoteWebhooksWithManager(ctrl); err != nil {
550552
logger.Error(err, "Error setting up note webhook")
551553
klog.FlushAndExit(klog.ExitFlushTimeout, 1)
552554
}
555+
if err := notesv1alpha1webhook.SetupClusterNoteWebhooksWithManager(ctrl); err != nil {
556+
logger.Error(err, "Error setting up clusternote webhook")
557+
klog.FlushAndExit(klog.ExitFlushTimeout, 1)
558+
}
553559

554560
projectCtrl := resourcemanagercontroller.ProjectController{
555561
ControlPlaneClient: ctrl.GetClient(),
@@ -734,7 +740,7 @@ func Run(ctx context.Context, c *config.CompletedConfig, opts *Options) error {
734740
klog.FlushAndExit(klog.ExitFlushTimeout, 1)
735741
}
736742

737-
noteCtrl := crmcontroller.NoteController{
743+
noteCtrl := notescontroller.NoteController{
738744
Client: ctrl.GetClient(),
739745
CreatorEditorRoleName: NoteCreatorEditorRoleName,
740746
CreatorEditorRoleNamespace: SystemNamespace,
@@ -744,6 +750,16 @@ func Run(ctx context.Context, c *config.CompletedConfig, opts *Options) error {
744750
klog.FlushAndExit(klog.ExitFlushTimeout, 1)
745751
}
746752

753+
clusterNoteCtrl := notescontroller.ClusterNoteController{
754+
Client: ctrl.GetClient(),
755+
CreatorEditorRoleName: NoteCreatorEditorRoleName,
756+
CreatorEditorRoleNamespace: SystemNamespace,
757+
}
758+
if err := clusterNoteCtrl.SetupWithManager(ctrl); err != nil {
759+
logger.Error(err, "Error setting up clusternote controller")
760+
klog.FlushAndExit(klog.ExitFlushTimeout, 1)
761+
}
762+
747763
reconciler := &remoteapiservicecontroller.RemoteAPIServiceAvailabilityReconciler{
748764
Client: ctrl.GetClient(),
749765
Reason: "Remote",

config/controller-manager/overlays/core-control-plane/rbac/role.yaml

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -45,25 +45,6 @@ rules:
4545
- subjectaccessreviews
4646
verbs:
4747
- create
48-
- apiGroups:
49-
- crm.miloapis.com
50-
resources:
51-
- notes
52-
verbs:
53-
- delete
54-
- get
55-
- list
56-
- patch
57-
- update
58-
- watch
59-
- apiGroups:
60-
- crm.miloapis.com
61-
resources:
62-
- notes/status
63-
verbs:
64-
- get
65-
- patch
66-
- update
6748
- apiGroups:
6849
- dns.networking.miloapis.com
6950
resources:
@@ -212,6 +193,27 @@ rules:
212193
- patch
213194
- update
214195
- watch
196+
- apiGroups:
197+
- notes.miloapis.com
198+
resources:
199+
- clusternotes
200+
- notes
201+
verbs:
202+
- delete
203+
- get
204+
- list
205+
- patch
206+
- update
207+
- watch
208+
- apiGroups:
209+
- notes.miloapis.com
210+
resources:
211+
- clusternotes/status
212+
- notes/status
213+
verbs:
214+
- get
215+
- patch
216+
- update
215217
- apiGroups:
216218
- notification.miloapis.com
217219
resources:
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
resources:
2-
- crm.miloapis.com_notes.yaml
1+
resources: []
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
---
2+
apiVersion: apiextensions.k8s.io/v1
3+
kind: CustomResourceDefinition
4+
metadata:
5+
annotations:
6+
controller-gen.kubebuilder.io/version: v0.18.0
7+
name: clusternotes.notes.miloapis.com
8+
spec:
9+
group: notes.miloapis.com
10+
names:
11+
kind: ClusterNote
12+
listKind: ClusterNoteList
13+
plural: clusternotes
14+
singular: clusternote
15+
scope: Cluster
16+
versions:
17+
- additionalPrinterColumns:
18+
- jsonPath: .spec.subjectRef.kind
19+
name: Subject Kind
20+
type: string
21+
- jsonPath: .spec.subjectRef.name
22+
name: Subject Name
23+
type: string
24+
- jsonPath: .spec.creatorRef.name
25+
name: Creator
26+
type: string
27+
- jsonPath: .status.conditions[?(@.type=='Ready')].status
28+
name: Ready
29+
type: string
30+
- jsonPath: .metadata.creationTimestamp
31+
name: Age
32+
type: date
33+
name: v1alpha1
34+
schema:
35+
openAPIV3Schema:
36+
description: |-
37+
ClusterNote is the Schema for the cluster-scoped notes API.
38+
It represents a note attached to a cluster-scoped subject resource.
39+
properties:
40+
apiVersion:
41+
description: |-
42+
APIVersion defines the versioned schema of this representation of an object.
43+
Servers should convert recognized schemas to the latest internal value, and
44+
may reject unrecognized values.
45+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
46+
type: string
47+
kind:
48+
description: |-
49+
Kind is a string value representing the REST resource this object represents.
50+
Servers may infer this from the endpoint the client submits requests to.
51+
Cannot be updated.
52+
In CamelCase.
53+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
54+
type: string
55+
metadata:
56+
type: object
57+
spec:
58+
description: NoteSpec defines the desired state of Note.
59+
properties:
60+
content:
61+
description: Content is the text content of the note.
62+
maxLength: 1000
63+
type: string
64+
creatorRef:
65+
description: |-
66+
CreatorRef is a reference to the user that created the note.
67+
Defaults to the user that created the note.
68+
properties:
69+
name:
70+
description: Name is the name of the User being referenced.
71+
type: string
72+
required:
73+
- name
74+
type: object
75+
x-kubernetes-validations:
76+
- message: creatorRef type is immutable
77+
rule: type(oldSelf) == null_type || self == oldSelf
78+
followUp:
79+
default: false
80+
description: |-
81+
FollowUp indicates whether this note requires follow-up.
82+
When true, the note is being actively tracked for further action.
83+
type: boolean
84+
interactionTime:
85+
description: InteractionTime is the timestamp of the interaction with
86+
the subject.
87+
format: date-time
88+
type: string
89+
nextAction:
90+
description: NextAction is an optional follow-up action.
91+
type: string
92+
nextActionTime:
93+
description: NextActionTime is the timestamp for the follow-up action.
94+
format: date-time
95+
type: string
96+
subjectRef:
97+
description: Subject is a reference to the subject of the note.
98+
properties:
99+
apiGroup:
100+
description: APIGroup is the group for the resource being referenced.
101+
type: string
102+
kind:
103+
description: Kind is the type of resource being referenced.
104+
type: string
105+
name:
106+
description: Name is the name of resource being referenced.
107+
type: string
108+
namespace:
109+
description: |-
110+
Namespace is the namespace of resource being referenced.
111+
Required for namespace-scoped resources. Omitted for cluster-scoped resources.
112+
type: string
113+
required:
114+
- apiGroup
115+
- kind
116+
- name
117+
type: object
118+
x-kubernetes-validations:
119+
- message: subject type is immutable
120+
rule: type(oldSelf) == null_type || self == oldSelf
121+
required:
122+
- content
123+
- subjectRef
124+
type: object
125+
status:
126+
description: NoteStatus defines the observed state of Note
127+
properties:
128+
conditions:
129+
default:
130+
- lastTransitionTime: "1970-01-01T00:00:00Z"
131+
message: Waiting for control plane to reconcile
132+
reason: Unknown
133+
status: Unknown
134+
type: Ready
135+
description: Conditions provide conditions that represent the current
136+
status of the Note.
137+
items:
138+
description: Condition contains details for one aspect of the current
139+
state of this API Resource.
140+
properties:
141+
lastTransitionTime:
142+
description: |-
143+
lastTransitionTime is the last time the condition transitioned from one status to another.
144+
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
145+
format: date-time
146+
type: string
147+
message:
148+
description: |-
149+
message is a human readable message indicating details about the transition.
150+
This may be an empty string.
151+
maxLength: 32768
152+
type: string
153+
observedGeneration:
154+
description: |-
155+
observedGeneration represents the .metadata.generation that the condition was set based upon.
156+
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
157+
with respect to the current state of the instance.
158+
format: int64
159+
minimum: 0
160+
type: integer
161+
reason:
162+
description: |-
163+
reason contains a programmatic identifier indicating the reason for the condition's last transition.
164+
Producers of specific condition types may define expected values and meanings for this field,
165+
and whether the values are considered a guaranteed API.
166+
The value should be a CamelCase string.
167+
This field may not be empty.
168+
maxLength: 1024
169+
minLength: 1
170+
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
171+
type: string
172+
status:
173+
description: status of the condition, one of True, False, Unknown.
174+
enum:
175+
- "True"
176+
- "False"
177+
- Unknown
178+
type: string
179+
type:
180+
description: type of condition in CamelCase or in foo.example.com/CamelCase.
181+
maxLength: 316
182+
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
183+
type: string
184+
required:
185+
- lastTransitionTime
186+
- message
187+
- reason
188+
- status
189+
- type
190+
type: object
191+
type: array
192+
createdBy:
193+
description: CreatedBy is the email of the user that created the note.
194+
type: string
195+
type: object
196+
type: object
197+
selectableFields:
198+
- jsonPath: .spec.creatorRef.name
199+
- jsonPath: .spec.subjectRef.name
200+
- jsonPath: .spec.subjectRef.kind
201+
- jsonPath: .spec.nextActionTime
202+
- jsonPath: .spec.followUp
203+
- jsonPath: .status.createdBy
204+
served: true
205+
storage: true
206+
subresources:
207+
status: {}

config/crd/bases/crm/crm.miloapis.com_notes.yaml renamed to config/crd/bases/notes/notes.miloapis.com_notes.yaml

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ kind: CustomResourceDefinition
44
metadata:
55
annotations:
66
controller-gen.kubebuilder.io/version: v0.18.0
7-
name: notes.crm.miloapis.com
7+
name: notes.notes.miloapis.com
88
spec:
9-
group: crm.miloapis.com
9+
group: notes.miloapis.com
1010
names:
1111
kind: Note
1212
listKind: NoteList
1313
plural: notes
1414
singular: note
15-
scope: Cluster
15+
scope: Namespaced
1616
versions:
1717
- additionalPrinterColumns:
1818
- jsonPath: .spec.subjectRef.kind
@@ -35,7 +35,7 @@ spec:
3535
openAPIV3Schema:
3636
description: |-
3737
Note is the Schema for the notes API.
38-
It represents a note attached to a subject (e.g. Contact or User).
38+
It represents a namespaced note attached to a subject resource.
3939
properties:
4040
apiVersion:
4141
description: |-
@@ -98,15 +98,9 @@ spec:
9898
properties:
9999
apiGroup:
100100
description: APIGroup is the group for the resource being referenced.
101-
enum:
102-
- iam.miloapis.com
103-
- notification.miloapis.com
104101
type: string
105102
kind:
106103
description: Kind is the type of resource being referenced.
107-
enum:
108-
- User
109-
- Contact
110104
type: string
111105
name:
112106
description: Name is the name of resource being referenced.

0 commit comments

Comments
 (0)