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

OS description attribute detector #1840

Conversation

nelsonghezzi
Copy link
Contributor

@nelsonghezzi nelsonghezzi commented Apr 24, 2021

This is a follow up of #1788 that I had in the works.

It adds the following resource options functions:

  • WithOSDescription: detects and adds the os.description attribute.
  • WithOS: detects and adds both os.type and os.description attributes.

Although the examples in the spec only shows a short, high level description, I thought it would be useful to include a bit more detail.

As the purpose of this attribute is to provide human-readable information, I think there's room to add details when it makes sense, and no implementation (processor, observability backend, etc) should make assumptions on its format or amount of information included.

Example captures

Here are some captures showing how the OS description is displayed for different OSes.

Windows

os_description_windows

Ubuntu (running natively on WSL2)

os_description_ubuntu_wsl

Alpine (container)

os_description_alpine

Debian (container)

os_description_debian

macOS

os_description_macos

Implementation details

A reference to golang.org/x/sys was added to access platform specific syscalls and functions.

The primary (and only) function consumed by the osDescriptionDetector is platformOSDescription(). This function returns a (string, error) with the description of the OS.

There are two definitions for this function: one for Unix-like OSes and other for Windows.

In the case of *NIX systems, the OS description is composed by two parts:

  • Unix name (uname): this is implemented the same for all *NIX systems thanks to the golang.org/x/sys package, which hides differences for systems which doesn't have a proper uname syscall (like Darwin). This part of the OS description it's assumed to exist on all these systems. Implemented as an uname() Go function.
  • OS Release information: this has two implementations, one for systems supporting os-release files (only tested on some linux distros, but it seems FreeBSD follows the same convention), and a separate impementation for Darwin, which has the release information in a different file and format. This part of the OS description is optional, meaning that if errors are found building the OS release string, it's not included in the final OS description. Implemented as an osRelease() Go function.

The implementation is distributed in the following files:

  • os_unix.go: it defines the platformOSDescription() and uname() functions, common for al *NIX systems. Build tags are included to target these common OSes.
  • os_release_unix.go: it defines the osRelease() for al *NIX systems except Darwin. The darwin tag is omitted.
  • os_release_darwin.go: it defines the osRelease() for Darwin.
  • os_windows.go: it defines the platformOSDescription() for Windows.

When it comes to os-release files, there are differences between distros on the number of properties present and the amount of detail in their values.

Based on the following examples:

I took some decisions on which properties to select to compose the final release description string, trying to maximize the amount of detail and avoiding redundancy. This is of course open to debate.

Tests for most unexported functions are TODO at the moment, but can/should be included.

Let me know what you think and any improvements you would make.

The function restoreProcessAttributesProviders was renamed to simply
restoreAttributesProviders to better reflect its broader scope, which
not only applies to process attribute's providers.
The suffix on os_linux.go was overriding the build tags already defined
in that file. The file was renamed to os_release_unix.go, reflecting
the main function defined in the file.

For consistency, os_darwin.go was renamed to os_release_darwing.go, as
its primary purpose is to also define the osRelease function.
@codecov
Copy link

codecov bot commented Apr 24, 2021

Codecov Report

Merging #1840 (1e7645f) into main (d8c9a95) will increase coverage by 0.0%.
The diff coverage is 81.6%.

Impacted file tree graph

@@          Coverage Diff          @@
##            main   #1840   +/-   ##
=====================================
  Coverage   72.7%   72.8%           
=====================================
  Files        164     166    +2     
  Lines       8081    8168   +87     
=====================================
+ Hits        5878    5949   +71     
- Misses      1974    1989   +15     
- Partials     229     230    +1     
Impacted Files Coverage Δ
sdk/resource/os_unix.go 74.0% <74.0%> (ø)
sdk/resource/os_release_unix.go 84.7% <84.7%> (ø)
sdk/resource/os.go 90.0% <85.7%> (-10.0%) ⬇️

@nelsonghezzi nelsonghezzi force-pushed the feature/os-description-attribute-detector branch from c3609bb to 256e537 Compare May 5, 2021 23:44
@nelsonghezzi
Copy link
Contributor Author

The only question I have (which shouldn't hold up this PR) is what the project policy is around how to handle OSes/architectures that don't fit into one of the "unix"/"windows" dichotomy, e.g. GOOS=plan9, GOOS=android. At the moment, cross-compiling for such an OS will fail here because there's no default definition for platformOSDescription.

Good catch. I think that our official stance is that we test on OS X, linux, and Windows, but I'm not sure that means we should knowingly break other platforms. @nelsonghezzi I assume it should be possible to have a default implementation of those functions that returns something indicating the value is unknown or causes the detector to not emit any attributes.

Yes, I agree! I mostly followed that line of thinking by leaving the build tags for the Unix version the same as the ones used by the golang.org/x/sys package (like this one), to not break the experience for someone trying to build on, for example, BSD.

But it's true I forgot about plan9 and android.

I think I prefer the approach of emitting an "unknown" OS description, as the error will be more clear upfront. I don't know if the project has a standard way to emit a warning on stdout (or if that's avoided altogether). If so, maybe we can also print something like WARN: os.description attribute is not supported on <GOOS>.

My current Go install (1.16) also list js (js/wasm) and illumos (illumos/amd64) as additional GOOSs. Doing a quick search, it looks that Illumos is a Unix-like OS based on OpenSolaris, but given that is not listed on the golang.org/x/sys build tags, I think it's better to handle it as unsupported.

I will work on this and fix the merge conflicts on the go.

nelsonghezzi and others added 3 commits June 10, 2021 03:22
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
@nelsonghezzi
Copy link
Contributor Author

I've added an os_unsupported.go file with a placeholder implementation of platformOSDescription.

Right now it doesn't include android or illumos GOOSs in the build tags because something unexpected happened while building the sdk module for those platforms (for example, with GOOS=android go build ./...):

# go.opentelemetry.io/otel/sdk/resource
resource/os_unsupported.go:23:6: platformOSDescription redeclared in this block
	previous declaration at resource/os_unix.go:43:39

It seems that android is aliased to linux, and illumos to solaris somehow 🤔. Made a quick check by removing the linux and solaris build tags from os_unix.go (while keeping android and illumos on os_unsupported.go) and the duplicate declaration error disappeared.

Makes sense? Good news is that as it's now, the project also compiles under these four GOOSs (and maybe even the OS description detector runs OK).

sdk/resource/os_release_darwin.go Outdated Show resolved Hide resolved
sdk/resource/os_release_darwin_test.go Outdated Show resolved Hide resolved
// properties of the os-release file. If no os-release file is found, or if the
// required properties to build the release description string are missing, an empty
// string is returned instead. For more information about os-release files, see:
// https://www.freedesktop.org/software/systemd/man/os-release.html
Copy link
Contributor

Choose a reason for hiding this comment

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

👍🏻 I love linked references!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Glad you liked it!

sdk/resource/os_release_unix.go Outdated Show resolved Hide resolved
sdk/resource/os_unix.go Outdated Show resolved Hide resolved
}

os.Remove(filename1)
os.Remove(filename2)
Copy link
Contributor

@cbandy cbandy Jun 19, 2021

Choose a reason for hiding this comment

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

These files aren't removed if the test fails. I'm not sure what Go versions we intend to support in tests, but

  • Go 1.14 has t.Cleanup() which acts a bit like defer. It will call something even if the test fails.
  • Go 1.15 has t.TempDir() which is automatically cleaned up.
  • Go 1.16 has effectively deprecated the ioutil package.

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 originally went with os.CreateTemp instead of ioutil.TempFile, but reverted back (725d90f) when the CI failed for Go < 1.16.

Go 1.14 was still listed on the project's README when I started to work on this, but it has been updated since then to reflect only the last two versions of Go as supported, so I guess it's safe to assume we can use t.TempDir().

I've updated the code based on your suggestion to use t.TempDir() here ea07b21, and realized I can use t.Cleanup() in a couple of other places (f94b783).

I will leave this thread open just in case a maintainer has something to comment on.

// See the License for the specific language governing permissions and
// limitations under the License.

// +build js plan9
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be the opposite of the known/supported tags? Like, "not darwin and not linux and not windows…"

The question I suppose is what should happen for a new or unexpected GOOS? By filling this file with negations, a new GOOS will successfully build using this file. Supporting it would be a matter of including its tag in its implementation and adding the negation 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.

I see your point here. The negation approach will be more future-proof, guaranteeing that the project will always build.

Including all but Linux, macOS and Windows (!linux,!darwin,!windows) on the os_unsupported file will be the right way to demonstrate on which platforms we tested on, and thus are confident the resource works.

On the other hand, I wasn't sure to preclude the availability of a working OS description for *BSD in that way. Altought I haven't tested on such platforms, technically it should work.

Going with the current approach, in the wake of a new GOOS, it shouldn't be much of a hassle to include it either, but it can be more daunting for someone that is just learning about the project to face a compilation error at first try.

I think if we go for the "strict negation" strategy, the other _unix files should be stripped from all but the linux and darwin tags too.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't mean to limit it to those three. I didn't want to type out everything in _unix in my question.

I don't have as strong opinion on what counts as "supported". Your choice to trust the sys/unix package makes sense to me.

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 don't mean to limit it to those three. I didn't want to type out everything in _unix in my question.

Oh, got it! Got confused for a sec because Linux, macOS and Windows are the platforms with a "compatibility guarantee" (as stated in the README) so I thought you were referring exclusively to them.

Pushed this change for others to also see the final form of this approach.

nelsonghezzi and others added 3 commits June 21, 2021 22:10
Co-authored-by: Chris Bandy <bandy.chris@gmail.com>
Co-authored-by: Chris Bandy <bandy.chris@gmail.com>
@nelsonghezzi
Copy link
Contributor Author

Hi! Just wanted to ask if we're OK to merge this one, or if it's preferred to postpone it after the v1.0.0 milestone.

I'm good either way, will check periodically to keep it merge-ready.

@Aneurysm9
Copy link
Member

Hi! Just wanted to ask if we're OK to merge this one, or if it's preferred to postpone it after the v1.0.0 milestone.

I'm good either way, will check periodically to keep it merge-ready.

We discussed in the last SIG meeting that we would like to minimize new feature additions in RCs, but I also don't want to keep PRs that are ready to go from landing. These detectors are going to land, whether that's in 1.0.0-RC# or 1.0.1 shouldn't matter too much. I'll poll the SIG at today's meeting and see if we have consensus to land it.

@MrAlias MrAlias merged commit 484258e into open-telemetry:main Jul 8, 2021
@jmacd
Copy link
Contributor

jmacd commented Jul 8, 2021

🎉

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.

7 participants