From 153b6319026cc3128e98302f67c31ca224523aab Mon Sep 17 00:00:00 2001 From: Elliott Baron Date: Fri, 14 Feb 2020 17:44:39 -0500 Subject: [PATCH] Add archive property, update service controller, disable flightrecorder controller --- .../rhjmc.redhat.com_flightrecorders_crd.yaml | 419 ++++++------------ .../crds/rhjmc.redhat.com_recordings_crd.yaml | 24 +- .../rhjmc/v1alpha1/flightrecorder_types.go | 1 + .../rhjmc/v1alpha2/flightrecorder_types.go | 4 +- pkg/apis/rhjmc/v1alpha2/recording_types.go | 29 +- .../rhjmc/v1alpha2/zz_generated.openapi.go | 23 +- .../flightrecorder_controller.go | 3 +- pkg/controller/service/service_controller.go | 67 +-- 8 files changed, 233 insertions(+), 337 deletions(-) diff --git a/deploy/crds/rhjmc.redhat.com_flightrecorders_crd.yaml b/deploy/crds/rhjmc.redhat.com_flightrecorders_crd.yaml index dc5d484ab..1f2dd6c18 100644 --- a/deploy/crds/rhjmc.redhat.com_flightrecorders_crd.yaml +++ b/deploy/crds/rhjmc.redhat.com_flightrecorders_crd.yaml @@ -12,292 +12,161 @@ spec: scope: Namespaced subresources: status: {} - version: v1alpha1 - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: FlightRecorder is the Schema for the flightrecorders API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: FlightRecorderSpec defines the desired state of FlightRecorder - properties: - port: - description: JMX port for target JVM - format: int32 - type: integer - recordingRequests: - description: Requests to create new flight recordings - items: - description: RecordingRequest allows the user to specify new recordings - for Container JFR to create - properties: - duration: - description: The requested total duration of the recording - type: string - eventOptions: - description: 'A list of event options to use when creating the - recording. These are used to enable and fine-tune individual - events. Examples: "jdk.ExecutionSample:enabled=true", "jdk.ExecutionSample:period=200ms"' - items: - type: string - type: array - name: - description: Name of the recording to be created - type: string - required: - - duration - - eventOptions - - name - type: object - type: array - required: - - port - - recordingRequests - type: object - status: - description: FlightRecorderStatus defines the observed state of FlightRecorder - properties: - recordings: - description: Lists all recordings for the pod/service that may be - downloaded - items: - description: RecordingInfo contains the status of recordings that - have already been created by Container JFR - properties: - active: - description: Whether the recording is currently running - type: boolean - downloadUrl: - description: A URL to download the JFR file for the recording - type: string - duration: - description: The duration of the recording specified during - creation - type: string - name: - description: Name of the created recording - type: string - startTime: - description: The date/time when the recording started - format: date-time - type: string - required: - - active - - duration - - name - - startTime - type: object - type: array - target: - description: Reference to the pod/service that this object controls - JFR for + validation: + openAPIV3Schema: + description: FlightRecorder is the Schema for the flightrecorders API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: FlightRecorderSpec defines the desired state of FlightRecorder + type: object + status: + description: FlightRecorderStatus defines the observed state of FlightRecorder + properties: + events: + description: Listing of events available in the target JVM + items: properties: - apiVersion: - description: API version of the referent. - type: string - fieldPath: - description: 'If referring to a piece of an object instead of - an entire object, this string should contain a valid JSON/Go - field access statement, such as desiredState.manifest.containers[2]. - For example, if the object reference is to a container within - a pod, this would take on a value like: "spec.containers{name}" - (where "name" refers to the name of the container that triggered - the event) or if no container name is specified "spec.containers[2]" - (container with index 2 in this pod). This syntax is chosen - only to have some well-defined way of referencing a part of - an object. TODO: this design is not final and this field is - subject to change in the future.' + category: + items: + type: string + type: array + description: type: string - kind: - description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + id: type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - namespace: - description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' - type: string - resourceVersion: - description: 'Specific resourceVersion to which this reference - is made, if any. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency' - type: string - uid: - description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' type: string - type: object - required: - - recordings - - target - type: object - type: object - served: true - storage: true - - name: v1alpha2 - schema: - openAPIV3Schema: - description: FlightRecorder is the Schema for the flightrecorders API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: FlightRecorderSpec defines the desired state of FlightRecorder - type: object - status: - description: FlightRecorderStatus defines the observed state of FlightRecorder - properties: - events: - description: Listing of events available in the target JVM - items: - properties: - category: - items: - type: string - type: array - description: - type: string - id: - type: string - name: - type: string - options: - additionalProperties: - properties: - defaultValue: - type: string - description: - type: string - name: - type: string - required: - - defaultValue - - description - - name - type: object - type: object - required: - - category - - description - - id - - name - - options - type: object - type: array - port: - description: JMX port for target JVM - format: int32 - type: integer - recordingSelector: - description: Recordings that match this selector belong to this FlightRecorder - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement is a selector that - contains values, a key, and an operator that relates the key - and values. + options: + additionalProperties: properties: - key: - description: key is the label key that the selector applies - to. + defaultValue: type: string - operator: - description: operator represents a key's relationship to - a set of values. Valid operators are In, NotIn, Exists - and DoesNotExist. + description: + type: string + name: type: string - values: - description: values is an array of string values. If the - operator is In or NotIn, the values array must be non-empty. - If the operator is Exists or DoesNotExist, the values - array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array required: - - key - - operator + - defaultValue + - description + - name type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single - {key,value} in the matchLabels map is equivalent to an element - of matchExpressions, whose key field is "key", the operator - is "In", and the values array contains only "value". The requirements - are ANDed. type: object + required: + - category + - description + - id + - name + - options type: object - target: - description: TODO Can we do this with labels/selectors instead? TODO - Need to potentially figure out how to manage this across both services - and pods in future Reference to the pod/service that this object - controls JFR for - properties: - apiVersion: - description: API version of the referent. - type: string - fieldPath: - description: 'If referring to a piece of an object instead of - an entire object, this string should contain a valid JSON/Go - field access statement, such as desiredState.manifest.containers[2]. - For example, if the object reference is to a container within - a pod, this would take on a value like: "spec.containers{name}" - (where "name" refers to the name of the container that triggered - the event) or if no container name is specified "spec.containers[2]" - (container with index 2 in this pod). This syntax is chosen - only to have some well-defined way of referencing a part of - an object. TODO: this design is not final and this field is - subject to change in the future.' - type: string - kind: - description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - namespace: - description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' - type: string - resourceVersion: - description: 'Specific resourceVersion to which this reference - is made, if any. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency' - type: string - uid: - description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: array + port: + description: JMX port for target JVM + format: int32 + minimum: 0 + type: integer + recordingSelector: + description: Recordings that match this selector belong to this FlightRecorder + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains + values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to a + set of values. Valid operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator + is In or NotIn, the values array must be non-empty. If the + operator is Exists or DoesNotExist, the values array must + be empty. This array is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: type: string - type: object - required: - - events - - port - - recordingSelector - - target - type: object - type: object + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator is + "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + target: + description: TODO Can we do this with labels/selectors instead? TODO + Need to potentially figure out how to manage this across both services + and pods in future Reference to the pod/service that this object controls + JFR for + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + required: + - events + - port + - recordingSelector + - target + type: object + type: object + version: v1alpha2 + versions: + - name: v1alpha2 served: true - storage: false + storage: true diff --git a/deploy/crds/rhjmc.redhat.com_recordings_crd.yaml b/deploy/crds/rhjmc.redhat.com_recordings_crd.yaml index f44267c4b..9aec7c1ac 100644 --- a/deploy/crds/rhjmc.redhat.com_recordings_crd.yaml +++ b/deploy/crds/rhjmc.redhat.com_recordings_crd.yaml @@ -31,9 +31,15 @@ spec: spec: description: RecordingSpec defines the desired state of Recording properties: + archive: + description: Whether this recording should be saved to persistent storage. + If true, the JFR file will be retained until this object is deleted. + If false, the JFR file will be deleted when its corresponding JVM + exits. + type: boolean duration: description: The requested total duration of the recording, a zero value - will record indefinitely + will record indefinitely. type: string eventOptions: description: 'A list of event options to use when creating the recording. @@ -43,19 +49,17 @@ spec: type: string type: array name: - description: Name of the recording to be created + description: Name of the recording to be created. type: string state: description: Desired state of the recording. If omitted, RUNNING will - be assumed TODO Should we not allow CREATED and STOPPING for this? - Do they make sense? + be assumed. enum: - - CREATED - RUNNING - - STOPPING - STOPPED type: string required: + - archive - duration - eventOptions - name @@ -64,17 +68,17 @@ spec: description: RecordingStatus defines the observed state of Recording properties: downloadURL: - description: A URL to download the JFR file for the recording + description: A URL to download the JFR file for the recording. type: string duration: - description: The duration of the recording specified during creation + description: The duration of the recording specified during creation. type: string startTime: - description: The date/time when the recording started + description: The date/time when the recording started. format: date-time type: string state: - description: Current state of the recording + description: Current state of the recording. enum: - CREATED - RUNNING diff --git a/pkg/apis/rhjmc/v1alpha1/flightrecorder_types.go b/pkg/apis/rhjmc/v1alpha1/flightrecorder_types.go index 6aac89313..ced5b768e 100644 --- a/pkg/apis/rhjmc/v1alpha1/flightrecorder_types.go +++ b/pkg/apis/rhjmc/v1alpha1/flightrecorder_types.go @@ -71,6 +71,7 @@ type RecordingInfo struct { // +k8s:openapi-gen=true // +kubebuilder:subresource:status // +kubebuilder:resource:path=flightrecorders,scope=Namespaced +// +kubebuilder:skipversion type FlightRecorder struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/pkg/apis/rhjmc/v1alpha2/flightrecorder_types.go b/pkg/apis/rhjmc/v1alpha2/flightrecorder_types.go index 1dc1f857e..f0a6042ba 100644 --- a/pkg/apis/rhjmc/v1alpha2/flightrecorder_types.go +++ b/pkg/apis/rhjmc/v1alpha2/flightrecorder_types.go @@ -31,6 +31,7 @@ type FlightRecorderStatus struct { // Reference to the pod/service that this object controls JFR for Target *corev1.ObjectReference `json:"target"` // JMX port for target JVM + // +kubebuilder:validation:Minimum=0 Port int32 `json:"port"` // Recordings that match this selector belong to this FlightRecorder RecordingSelector *metav1.LabelSelector `json:"recordingSelector"` @@ -59,7 +60,8 @@ type OptionDescriptor struct { // +k8s:openapi-gen=true // +kubebuilder:subresource:status // +kubebuilder:resource:path=flightrecorders,scope=Namespaced -type FlightRecorder struct { +// +kubebuilder:storageversion +type FlightRecorder struct { // TODO Conversion with v1alpha1? Might not be possible due to moving data to Recording metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/pkg/apis/rhjmc/v1alpha2/recording_types.go b/pkg/apis/rhjmc/v1alpha2/recording_types.go index 770fcd3f8..bc27101ca 100644 --- a/pkg/apis/rhjmc/v1alpha2/recording_types.go +++ b/pkg/apis/rhjmc/v1alpha2/recording_types.go @@ -13,20 +13,23 @@ type RecordingSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file // Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html + // TODO Full validation marker list: https://book.kubebuilder.io/reference/markers/crd-validation.html - // Name of the recording to be created + // Name of the recording to be created. Name string `json:"name"` // A list of event options to use when creating the recording. // These are used to enable and fine-tune individual events. // Examples: "jdk.ExecutionSample:enabled=true", "jdk.ExecutionSample:period=200ms" // +listType=set EventOptions []string `json:"eventOptions"` // TODO Maybe replace with more specific type (e.g. "typeID, option, value" tuples) - // The requested total duration of the recording, a zero value will record indefinitely + // The requested total duration of the recording, a zero value will record indefinitely. Duration metav1.Duration `json:"duration"` - // Desired state of the recording. If omitted, RUNNING will be assumed - // TODO Should we not allow CREATED and STOPPING for this? Do they make sense? - // +kubebuilder:validation:Enum=CREATED;RUNNING;STOPPING;STOPPED + // Desired state of the recording. If omitted, RUNNING will be assumed. + // +kubebuilder:validation:Enum=RUNNING;STOPPED State RecordingState `json:"state,omitempty"` + // Whether this recording should be saved to persistent storage. If true, the JFR file will be retained until + // this object is deleted. If false, the JFR file will be deleted when its corresponding JVM exits. + Archive bool `json:"archive"` } // RecordingState describes the current state of the recording according @@ -35,16 +38,16 @@ type RecordingState string // FIXME From client/command_types.go const ( // RecordingStateCreated means the recording has been accepted, but - // has not started yet + // has not started yet. RecordingStateCreated RecordingState = "CREATED" // RecordingStateRunning means the recording has started and is - // currently running + // currently running. RecordingStateRunning RecordingState = "RUNNING" // RecordingStateStopping means that the recording is in the process - // of finishing + // of finishing. RecordingStateStopping RecordingState = "STOPPING" // RecordingStateStopped means the recording has completed and the - // JFR file is fully written + // JFR file is fully written. RecordingStateStopped RecordingState = "STOPPED" ) @@ -55,14 +58,14 @@ type RecordingStatus struct { // Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file // Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html - // Current state of the recording + // Current state of the recording. // +kubebuilder:validation:Enum=CREATED;RUNNING;STOPPING;STOPPED State RecordingState `json:"state"` - // The date/time when the recording started + // The date/time when the recording started. StartTime metav1.Time `json:"startTime"` - // The duration of the recording specified during creation + // The duration of the recording specified during creation. Duration metav1.Duration `json:"duration"` // FIXME Needed? - // A URL to download the JFR file for the recording + // A URL to download the JFR file for the recording. DownloadURL string `json:"downloadURL,omitempty"` // TODO Consider adding Conditions: // https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties diff --git a/pkg/apis/rhjmc/v1alpha2/zz_generated.openapi.go b/pkg/apis/rhjmc/v1alpha2/zz_generated.openapi.go index 4d1af333e..4fef3edbc 100644 --- a/pkg/apis/rhjmc/v1alpha2/zz_generated.openapi.go +++ b/pkg/apis/rhjmc/v1alpha2/zz_generated.openapi.go @@ -181,7 +181,7 @@ func schema_pkg_apis_rhjmc_v1alpha2_RecordingSpec(ref common.ReferenceCallback) Properties: map[string]spec.Schema{ "name": { SchemaProps: spec.SchemaProps{ - Description: "Name of the recording to be created", + Description: "Name of the recording to be created.", Type: []string{"string"}, Format: "", }, @@ -207,19 +207,26 @@ func schema_pkg_apis_rhjmc_v1alpha2_RecordingSpec(ref common.ReferenceCallback) }, "duration": { SchemaProps: spec.SchemaProps{ - Description: "The requested total duration of the recording, a zero value will record indefinitely", + Description: "The requested total duration of the recording, a zero value will record indefinitely.", Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"), }, }, "state": { SchemaProps: spec.SchemaProps{ - Description: "Desired state of the recording. If omitted, RUNNING will be assumed", + Description: "Desired state of the recording. If omitted, RUNNING will be assumed.", Type: []string{"string"}, Format: "", }, }, + "archive": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this recording should be saved to persistent storage. If true, the JFR file will be retained until this object is deleted. If false, the JFR file will be deleted when its corresponding JVM exits.", + Type: []string{"boolean"}, + Format: "", + }, + }, }, - Required: []string{"name", "eventOptions", "duration"}, + Required: []string{"name", "eventOptions", "duration", "archive"}, }, }, Dependencies: []string{ @@ -236,26 +243,26 @@ func schema_pkg_apis_rhjmc_v1alpha2_RecordingStatus(ref common.ReferenceCallback Properties: map[string]spec.Schema{ "state": { SchemaProps: spec.SchemaProps{ - Description: "Current state of the recording", + Description: "Current state of the recording.", Type: []string{"string"}, Format: "", }, }, "startTime": { SchemaProps: spec.SchemaProps{ - Description: "The date/time when the recording started", + Description: "The date/time when the recording started.", Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), }, }, "duration": { SchemaProps: spec.SchemaProps{ - Description: "The duration of the recording specified during creation", + Description: "The duration of the recording specified during creation.", Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"), }, }, "downloadURL": { SchemaProps: spec.SchemaProps{ - Description: "A URL to download the JFR file for the recording", + Description: "A URL to download the JFR file for the recording.", Type: []string{"string"}, Format: "", }, diff --git a/pkg/controller/flightrecorder/flightrecorder_controller.go b/pkg/controller/flightrecorder/flightrecorder_controller.go index c567624f3..1341a33ae 100644 --- a/pkg/controller/flightrecorder/flightrecorder_controller.go +++ b/pkg/controller/flightrecorder/flightrecorder_controller.go @@ -33,7 +33,8 @@ var log = logf.Log.WithName("controller_flightrecorder") // Add creates a new FlightRecorder Controller and adds it to the Manager. The Manager will set fields on the Controller // and Start it when the Manager is Started. func Add(mgr manager.Manager) error { - return add(mgr, newReconciler(mgr)) + //return add(mgr, newReconciler(mgr)) + return nil // FIXME } // newReconciler returns a new reconcile.Reconciler diff --git a/pkg/controller/service/service_controller.go b/pkg/controller/service/service_controller.go index be5c45034..93b83d6e5 100644 --- a/pkg/controller/service/service_controller.go +++ b/pkg/controller/service/service_controller.go @@ -4,7 +4,7 @@ import ( "context" "errors" - rhjmcv1alpha1 "github.com/rh-jmc-team/container-jfr-operator/pkg/apis/rhjmc/v1alpha1" + rhjmcv1alpha2 "github.com/rh-jmc-team/container-jfr-operator/pkg/apis/rhjmc/v1alpha2" corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -49,7 +49,7 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error { } // Watch for changes to secondary resource FlightRecorder and requeue the owner Service - err = c.Watch(&source.Kind{Type: &rhjmcv1alpha1.FlightRecorder{}}, &handler.EnqueueRequestForOwner{ + err = c.Watch(&source.Kind{Type: &rhjmcv1alpha2.FlightRecorder{}}, &handler.EnqueueRequestForOwner{ IsController: true, OwnerType: &corev1.Service{}, }) @@ -99,25 +99,26 @@ func (r *ReconcileService) Reconcile(request reconcile.Request) (reconcile.Resul jmxPort, err := getServiceJMXPort(svc) jmxCompatible := err == nil - // Define a new FlightRecorder object for this service - jfr, err := r.newFlightRecorderForService(svc) - if err != nil { - return reconcile.Result{}, err - } - - // Set Service instance as the owner and controller - if err := controllerutil.SetControllerReference(svc, jfr, r.scheme); err != nil { - return reconcile.Result{}, err - } - // Check if this FlightRecorder already exists - found := &rhjmcv1alpha1.FlightRecorder{} - err = r.client.Get(ctx, types.NamespacedName{Name: jfr.Name, Namespace: jfr.Namespace}, found) + found := &rhjmcv1alpha2.FlightRecorder{} + jfrName := svc.Name + err = r.client.Get(ctx, types.NamespacedName{Name: jfrName, Namespace: request.Namespace}, found) if err != nil && kerrors.IsNotFound(err) { if jmxCompatible { - reqLogger.Info("Creating a new FlightRecorder", "Namespace", jfr.Namespace, "Name", jfr.Name) - jfr.Spec.Port = jmxPort + reqLogger.Info("Creating a new FlightRecorder", "Namespace", request.Namespace, "Name", jfrName) + + // Define a new FlightRecorder object for this service + jfr, err := r.newFlightRecorderForService(jfrName, svc, jmxPort) + if err != nil { + return reconcile.Result{}, err + } + + // Set Service instance as the owner and controller + if err := controllerutil.SetControllerReference(svc, jfr, r.scheme); err != nil { + return reconcile.Result{}, err + } + err = r.client.Create(ctx, jfr) if err != nil { return reconcile.Result{}, err @@ -141,7 +142,7 @@ func (r *ReconcileService) Reconcile(request reconcile.Request) (reconcile.Resul if !jmxCompatible { // Service was previously JMX-compatible but is not anymore, // so delete its FlightRecorder and do not requeue a new one - reqLogger.Info("Deleting dangling FlightRecorder", "Namespace", jfr.Namespace, "Name", jfr.Name) + reqLogger.Info("Deleting dangling FlightRecorder", "Namespace", request.Namespace, "Name", jfrName) err = r.client.Delete(ctx, found) if err != nil { return reconcile.Result{}, err @@ -149,10 +150,10 @@ func (r *ReconcileService) Reconcile(request reconcile.Request) (reconcile.Resul return reconcile.Result{}, nil } - if found.Spec.Port != jmxPort { + if found.Status.Port != jmxPort { // FlightRecorder is incorrect - service was likely modified. Delete outdated FlightRecorder // and requeue creation of corrected one - reqLogger.Info("Deleting outdated FlightRecorder", "Namespace", jfr.Namespace, "Name", jfr.Name) + reqLogger.Info("Deleting outdated FlightRecorder", "Namespace", request.Namespace, "Name", jfrName) err = r.client.Delete(ctx, found) if err != nil { return reconcile.Result{}, err @@ -184,7 +185,8 @@ func getServiceJMXPort(svc *corev1.Service) (int32, error) { } // newFlightRecorderForService returns a FlightRecorder with the same name/namespace as the service -func (r *ReconcileService) newFlightRecorderForService(svc *corev1.Service) (*rhjmcv1alpha1.FlightRecorder, error) { +func (r *ReconcileService) newFlightRecorderForService(name string, svc *corev1.Service, jmxPort int32) (*rhjmcv1alpha2.FlightRecorder, error) { + // Inherit "app" label from service appLabel := svc.Name // Use service name as fallback if label, pres := svc.Labels["app"]; pres { appLabel = label @@ -192,22 +194,29 @@ func (r *ReconcileService) newFlightRecorderForService(svc *corev1.Service) (*rh labels := map[string]string{ "app": appLabel, } + + // Add reference to service to this FlightRecorder ref, err := reference.GetReference(r.scheme, svc) if err != nil { return nil, err } - return &rhjmcv1alpha1.FlightRecorder{ // TODO should we use OwnerReference for this? + + // Use label selector matching the name of this FlightRecorder + selector := &metav1.LabelSelector{} + selector = metav1.AddLabelToSelector(selector, rhjmcv1alpha2.RecordingLabel, name) + + return &rhjmcv1alpha2.FlightRecorder{ ObjectMeta: metav1.ObjectMeta{ - Name: svc.Name, + Name: name, Namespace: svc.Namespace, Labels: labels, }, - Spec: rhjmcv1alpha1.FlightRecorderSpec{ - RecordingRequests: []rhjmcv1alpha1.RecordingRequest{}, - }, - Status: rhjmcv1alpha1.FlightRecorderStatus{ - Target: ref, - Recordings: []rhjmcv1alpha1.RecordingInfo{}, + Spec: rhjmcv1alpha2.FlightRecorderSpec{}, + Status: rhjmcv1alpha2.FlightRecorderStatus{ + Events: []rhjmcv1alpha2.EventInfo{}, + Target: ref, + Port: jmxPort, + RecordingSelector: selector, }, }, nil }