-
Notifications
You must be signed in to change notification settings - Fork 440
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
Expose container ports on the collector pod #1070
Expose container ports on the collector pod #1070
Conversation
17c405b
to
ede60ac
Compare
pkg/collector/parser/receiver.go
Outdated
// ignore kubeletstats receiver as it holds the field key endpoint, and it | ||
// is a scraper, we only expose endpoint through k8s service objects for | ||
// receivers that aren't scrapers. | ||
case name == "kubeletstats": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO this there should be a config option, with sane defaults, to control which receivers types should be ignored.
I'm thinking about custom build instances of the collector which could contain receivers not available in contrib.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
singleContainerPortFromConfigEndpoint()
is based on singlePortFromConfigEndpoint()
so both functions have this logic. Would you prefer that I add configuration for this in this PR? I'm also happy to open an issue and fix this separately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pavolloffay What do you think about making this configurable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO this should be a separate issue given this logic is already in the operator. The concern is valid, but i think adding the logic to this PR is going to expand the scope of this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I created an issue for this:
#1095
@@ -81,6 +81,13 @@ type OpenTelemetryCollectorSpec struct { | |||
// +optional | |||
// +listType=atomic | |||
Ports []v1.ServicePort `json:"ports,omitempty"` | |||
// ContainerPorts allows a set of ports to be exposed by the underlying v1.Container. By default, the operator | |||
// will attempt to infer the required ports by parsing the .Spec.Config property but this property can be | |||
// used to open additional ports that can't be inferred by the operator, like for custom receivers. Any ports |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
like for custom receivers.
I would assume that custom receiver ports should be defined in the spec.ports
and the container ports are rather used for the ports that should not be exposed in the service.
Do we even need to have service ports and container ports? We could use the service ports to open the ports on the container. Is there any use-case when container ports != service ports?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure - I included this for the flexibility and because the types are different. I can change it so we parse the configured service ports into container ports if that's the direction we want to go in?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pavolloffay, do you want me to change the logic to parse the container ports from the list of service ports? Or are you waiting for others' input on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there is no hard requirement to expose additional configuration in the CR then I would opt-in for deriving the ports automatically for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kevinearls could you please review as well? You were looking into this in the past.
pkg/collector/container.go
Outdated
nameErrs := validation.IsValidPortName(truncName) | ||
numErrs := validation.IsValidPortNum(int(p.Port)) | ||
if len(nameErrs) > 0 || len(numErrs) > 0 { | ||
logger.Error(errInvalidPort, "dropping container port", "port.name", truncName, "port.num", p.Port, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should rather emit k8s event to let the user know that the validation failed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kevinearls you did some logging-related work recently.
Do you think we should use k8s events here or normal logs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pavolloffay I don't really know, to be honest. I think logging would be fine but we are not very consistent about this.
// Container builds a container for the given collector. | ||
func Container(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector) corev1.Container { | ||
image := otelcol.Spec.Image | ||
if len(image) == 0 { | ||
image = cfg.CollectorImage() | ||
} | ||
|
||
// build container ports from service ports | ||
ports := getConfigContainerPorts(logger, otelcol.Spec.Config) | ||
for _, p := range otelcol.Spec.Ports { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function does a couple of different things:
- name truncation
- validation
- create a container port
The truncation and validation should be done in the defaulting and validating webhook. The otelcol spec received in this function should be valid and prepared for use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The validating webhook can as well return user-facing error if the name validation fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't add the current truncation to the defaulting webhook because it causes an import cycle:
package github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1
imports github.com/open-telemetry/opentelemetry-operator/pkg/naming
imports github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1: import cycle not allowed
Is it reasonable for us to error out when a Spec Port is too long (like any other port naming error)? Or should I truncate a different way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the user can change the spec in a meaningful way to meet the requirements than returning the error is fine
pkg/collector/container_test.go
Outdated
specPorts: []corev1.ServicePort{ | ||
{ | ||
// this port name contains a non alphanumeric character, which is invalid. | ||
Name: "-test🦄port", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the only change to be made is move some of the validation / mutation to the corresponding webhooks, otherwise this looks great. Awesome stuff 🥳
ed81ddc
to
dcdd998
Compare
assert.NoError(t, err) | ||
return | ||
} | ||
assert.ErrorContains(t, err, test.expectedErr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is error checking against a string value which isn't ideal. Some other options are:
- Create errors that are wrapped in the returned
fmt.Errorf
so that I can useassert.ErrorIs()
. - Check that the error isn't nil instead of what its value is:
assert.Error()
.
truncName := naming.Truncate(p.Name, maxPortLen) | ||
if p.Name != truncName { | ||
logger.Info("truncating container port name", | ||
"port.name.prev", p.Name, "port.name.new", truncName) | ||
} | ||
nameErrs := validation.IsValidPortName(truncName) | ||
numErrs := validation.IsValidPortNum(int(p.Port)) | ||
if len(nameErrs) > 0 || len(numErrs) > 0 { | ||
logger.Error(errInvalidPort, "dropping container port", "port.name", truncName, "port.num", p.Port, | ||
"port.name.errs", nameErrs, "num.errs", numErrs) | ||
continue | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be able to move this truncation+validation to inside the adapters.ConfigToReceiverPorts()
function - what do you think, @pavolloffay?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
validation and truncation can definitely live in other package to avoid import cycle
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to move it and it caused an import cycle. 😞 So I'm just going to leave the code as it is.
// validate autoscale with horizontal pod autoscaler | ||
if r.Spec.MaxReplicas != nil { | ||
if *r.Spec.MaxReplicas < int32(1) { | ||
if *r.Spec.MaxReplicas <= int32(1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed this to match the error string explanation (max replicas should be more than one) but I don't think there's a problem if maxReplicas is 1? I can change the error string instead of changing this expression if others agree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not aware of any limitations regarding MaxReplicas
and I think rather the error message should be changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be ideally changed in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved: #1136
I will remove these changes from this PR.
// validate autoscale with horizontal pod autoscaler | ||
if r.Spec.MaxReplicas != nil { | ||
if *r.Spec.MaxReplicas < int32(1) { | ||
if *r.Spec.MaxReplicas <= int32(1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not aware of any limitations regarding MaxReplicas
and I think rather the error message should be changed.
fe921c2
to
322ee43
Compare
* expose container ports * get container ports from service ports * add, fix tests * move logic changes to their own branch
fixes #1011, adding a way to expose container ports for service and pod monitoring.
Uses functions from this package to validate port name and number. If the name is too long, we cut it off to fit.