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

Add k8s observer #185

Merged
merged 14 commits into from
Apr 29, 2020
Merged

Add k8s observer #185

merged 14 commits into from
Apr 29, 2020

Conversation

jrcamp
Copy link
Contributor

@jrcamp jrcamp commented Apr 23, 2020

This adds a k8s observer as well as reworking some of the observer data
structures. Work is still ongoing to add an end to end test to validate it
against a real k8s instance and dynamically start a real receiver.

Before Endpoint was an interface with methods like ID(), Target(), etc.
but these had a smell because they were acting as simple data getters for
struct values. This changes Endpoint to be a struct itself that has members
like ID and Target. It has a member Details that can currently either be
a Pod (ie just has an IP address) or a Port (has a pod IP but also an
associated port). Currently this Details is an interface{} as there are
currently no methods in which to base an interface off of. This is basically
due to a limitation in Go which doesn't have true class inheritance. It is no
worse than before since when Endpoint was an interface the consumer would
have to type switch to check what concrete type it was (pod, port, etc.). I
have tried structuring it many ways and this is the least worse I've found thus
far.

This adds a k8s observer as well as reworking some of the observer data
structures. Work is still ongoing to add an end to end test to validate it
against a real k8s instance and dynamically start a real receiver.

Before Endpoint was an interface with methods like `ID()`, `Target()`, etc.
but these had a smell because they were acting as simple data getters for
struct values. This changes Endpoint to be a struct itself that has members
like `ID` and `Target`. It has a member `Details` that can currently either be
a Pod (ie just has an IP address) or a Port (has a pod IP but also an
associated port). Currently this `Details` is an `interface{}` as there are
currently no methods in which to base an interface off of. This is basically
due to a limitation in Go which doesn't have true class inheritance. It is no
worse than before since when `Endpoint` was an interface the consumer would
have to type switch to check what concrete type it was (pod, port, etc.). I
have tried structuring it many ways and this is the least worse I've found thus
far.
type Config struct {
configmodels.ExtensionSettings `mapstructure:",squash"`

// For example, node name can be passed to each agent with the downward API as follows
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is a "downward API"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 27 to 33
// env:
// - name: K8S_NODE_NAME
// valueFrom:
// fieldRef:
// fieldPath: spec.nodeName
//
// Then set this value to ${K8S_NODE_NAME}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand this example. Please clarify. What does env signify here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's part of a pod spec. Should make more sense based on the link above but I can expand the comment.

return &Factory{createK8sConfig: func() (*rest.Config, error) {
restConfig, err := rest.InClusterConfig()
if err != nil {
return nil, fmt.Errorf("failed creating Kubernetes in-cluster REST config: %v", err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears that this will result in "failed creating Kubernetes in-cluster REST config" repeated twice when called from CreateExtension()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

extension/observer/k8s/handler.go Outdated Show resolved Hide resolved

// OnAdd is called in response to a pod being added.
func (h *handler) OnAdd(obj interface{}) {
pod := obj.(*v1.Pod)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it guaranteed for obj to be v1.Pod? Perhaps be defensive and check?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The kubernetes cache syncers are of a single type and we only sync pods so not too worried about any other type sneaking in.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would still be careful. I assume checking the interface has no or little cost. I get an eye pain from this unprotected conversion :-)
If for some reason you do not want to add the check I would document that obj is expected to be of particular interface and why the expectation.

oldEndpoints := map[string]observer.Endpoint{}
newEndpoints := map[string]observer.Endpoint{}

for _, e := range h.convertPodToEndpoints(oldObj.(*v1.Pod)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. Maybe be defensive and check the type assertion?


var removedEndpoints, updatedEndpoints, addedEndpoints []observer.Endpoint

for _, e := range newEndpoints {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to add a comment to explain what this loop is doing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Labels() map[string]string
Target string
// Details contains additional context about the endpoint such as a Pod or Port.
Details interface{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you mentioned this in the PR but just to double check is there any trait that Details must exhibit that we can define an interface on? Perhaps it has to have a Name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in the future it might have something like ToMap() map[string]string that returns a map of the object using semantic conventions but it's not needed yet. If something like Name would be common across all Details then it would go into the Endpoint instead I think. I considered Type() but that's really just duplicating the Go type so not much help.

jrcamp and others added 3 commits April 24, 2020 16:05
Co-Authored-By: Tigran Najaryan <4194920+tigrannajaryan@users.noreply.github.com>
// See the License for the specific language governing permissions and
// limitations under the License.

package k8s
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use k8s_observer as package

@tigrannajaryan
Copy link
Member

Please resolve the conflict.

Copy link
Member

@tigrannajaryan tigrannajaryan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, except a couple unchecked interface type assertions, which make me a bit uncomfortable and appear to be against our "don't crash" recommendation: https://github.com/open-telemetry/opentelemetry-collector/blob/master/CONTRIBUTING.md#do-not-crash-after-startup


// OnAdd is called in response to a pod being added.
func (h *handler) OnAdd(obj interface{}) {
pod := obj.(*v1.Pod)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would still be careful. I assume checking the interface has no or little cost. I get an eye pain from this unprotected conversion :-)
If for some reason you do not want to add the check I would document that obj is expected to be of particular interface and why the expectation.

@jrcamp
Copy link
Contributor Author

jrcamp commented Apr 28, 2020

LGTM, except a couple unchecked interface type assertions, which make me a bit uncomfortable and appear to be against our "don't crash" recommendation: https://github.com/open-telemetry/opentelemetry-collector/blob/master/CONTRIBUTING.md#do-not-crash-after-startup

I will change it so you can sleep at night. :-P

@tigrannajaryan
Copy link
Member

LGTM, except a couple unchecked interface type assertions, which make me a bit uncomfortable and appear to be against our "don't crash" recommendation: https://github.com/open-telemetry/opentelemetry-collector/blob/master/CONTRIBUTING.md#do-not-crash-after-startup

I will change it so you can sleep at night. :-P

Thank you! :-)

Copy link
Member

@tigrannajaryan tigrannajaryan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix the build (something about goimports).

@jrcamp
Copy link
Contributor Author

jrcamp commented Apr 28, 2020

@tigrannajaryan build is green now

@tigrannajaryan
Copy link
Member

@bogdandrutu are you OK or you need to review more?

@bogdandrutu
Copy link
Member

Ship it :)

@bogdandrutu bogdandrutu merged commit ba49ec3 into open-telemetry:master Apr 29, 2020
wyTrivail referenced this pull request in mxiamxia/opentelemetry-collector-contrib Jul 13, 2020
* Add k8s observer

This adds a k8s observer as well as reworking some of the observer data
structures. Work is still ongoing to add an end to end test to validate it
against a real k8s instance and dynamically start a real receiver.

Before Endpoint was an interface with methods like `ID()`, `Target()`, etc.
but these had a smell because they were acting as simple data getters for
struct values. This changes Endpoint to be a struct itself that has members
like `ID` and `Target`. It has a member `Details` that can currently either be
a Pod (ie just has an IP address) or a Port (has a pod IP but also an
associated port). Currently this `Details` is an `interface{}` as there are
currently no methods in which to base an interface off of. This is basically
due to a limitation in Go which doesn't have true class inheritance. It is no
worse than before since when `Endpoint` was an interface the consumer would
have to type switch to check what concrete type it was (pod, port, etc.). I
have tried structuring it many ways and this is the least worse I've found thus
far.

* fix go.mod

* remove e2e test that is not ready

* Apply suggestions from code review

Co-Authored-By: Tigran Najaryan <4194920+tigrannajaryan@users.noreply.github.com>

* review updates

* review updates

* rename package

* cleanup deps

* fix type changes

* fix linting

* fix build

Co-authored-by: Tigran Najaryan <4194920+tigrannajaryan@users.noreply.github.com>
mxiamxia referenced this pull request in mxiamxia/opentelemetry-collector-contrib Jul 22, 2020
* Add license automatically

* Add addlicense to make default goal

* Add dependency to internal tools
straussb pushed a commit to straussb/opentelemetry-collector-contrib that referenced this pull request Mar 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants