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

metrics: add build command duration metric #2225

Merged
merged 1 commit into from
Feb 14, 2024

Conversation

jsternberg
Copy link
Collaborator

This adds a build duration metric for the build command with attributes
related to the buildx driver, the error type (if any), and which options
were used to perform the build from a subset of the options.


import "golang.org/x/sys/windows"
Copy link
Member

Choose a reason for hiding this comment

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

Looks missing

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch. I'll have to watch for that in the future. I didn't realize my IDE wasn't running goimports on files that didn't match my build constraints.

@jsternberg jsternberg force-pushed the command-duration-metric branch 4 times, most recently from 99bea90 to 43ffe95 Compare February 1, 2024 18:10

// getVCSPath returns the vcs:source attribute for a context path.
func getVCSPath(contextPath string) string {
var wd string
Copy link
Member

@tonistiigi tonistiigi Feb 2, 2024

Choose a reason for hiding this comment

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

Name should remain contextPath. Otherwise it is confusing why wd is passed to git command.

return o.result
}

// getVCSPath returns the vcs:source attribute for a context path.
Copy link
Member

Choose a reason for hiding this comment

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

The comment is wrong as this does not share implementation with vcs:source. Atm it would be "returns git remote URL". Rename the function as well as this isn't "Path".

func getVCSPath(contextPath string) string {
var wd string
if filepath.IsAbs(contextPath) {
wd = contextPath
Copy link
Member

Choose a reason for hiding this comment

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

contextPath in here can be - or a URL.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The getGitAttributes function in build/git.go doesn't seem to handle this case. Does that need to be fixed too? It seems to get passed these parameters directly.

This code will also just fail and ignore the error if the git commands don't succeed. Is that suitable or do we need to catch the URL?

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've updated this to include a Stat call before the logic. That should catch any paths that don't make sense before we start to try running git commands.

Copy link
Member

@crazy-max crazy-max Feb 6, 2024

Choose a reason for hiding this comment

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

contextPath in here can be - or a URL.

Hum right this was never checked when getting Git attributes.

@@ -279,6 +362,7 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
return err
}

done := timeBuildCommand(mp, options.Attributes(dockerCli))
Copy link
Member

Choose a reason for hiding this comment

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

Make this (build)metricsAttributes(b, dockerCli, options) attribute.Set.

Don't need options.newBuilder() or other methods on options.

Inside the function, read the b.Driver directly from input without the struct/once. If you want then for the hash you can leave the lazy evaluation, but for just reading the driver property, there is no need for this overhead.

The call could also be inside timeBuildCommand.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Made the change. This isn't called inside of timeBuildCommand as there's another section of code that's going to use these same attributes in another PR.

func timeBuildCommand(mp metric.MeterProvider, attrs attribute.Set) func(err error) {
meter := metricutil.Meter(mp)
counter, _ := meter.Float64Counter("command.time",
metric.WithDescription("Measures the duration of the buildx command."),
Copy link
Member

Choose a reason for hiding this comment

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

"build command" seems more appropriate as looks specific to that command atm.


// errorType returns an attribute for the error type based on the error category.
// If nil, this function returns an invalid attribute.
func errorType(err error) string {
Copy link
Member

Choose a reason for hiding this comment

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

otelErrorType/metricsErrorType maybe to make it clearer what "type" this is.

func (o *buildOptionsHash) String() string {
o.resultOnce.Do(func() {
target := o.target
contextPath := o.contextPath
Copy link
Member

@tonistiigi tonistiigi Feb 2, 2024

Choose a reason for hiding this comment

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

These paths need to be normalized so they always generate same hash(Eg. made absolute).

As this is leaking the system paths and Git info it still needs the salt like #2185 (comment) . The multi-machine case doesn't apply anyway as system paths are different, and I don't think there is a way to implement it safely with some other way.

If the path is a URL then the URL should go into hash. If it is - then maybe workdir is better than just const character? (but it needs salt)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Since this code is in a new place, I don't have easy access to the node identifier anymore. I'd have to make that function public.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've also now changed this so it will attempt to transform context path into a relative path. If a git directory is present, the relative path will be dependent on the root directory of the git repository.

func getGitRemoteURL(contextPath string) string {
// Check if this is a valid path to begin with before attempting
// to run git. It might be a non-filesystem URL.
if _, err := os.Stat(contextPath); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

I think we should do the URL/- detection to match LoadInputs and unless I'm missing something, same validation would need to be called for the vcs codepath.

If you think this is outside of scope, then create a follow-up issue for it and assign to yourself.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I took a look at LoadInputs and it checks if the path is a local directory before checking for a URL. It seems to use this same code for that same functionality so I just exposed it and used that function instead. I've also included an explicit check for - to avoid attempting to call stat on that path and to avoid performing this logic at all in those circumstances.

We'll only do git remote url detection when it's a local directory.

Copy link
Member

Choose a reason for hiding this comment

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

@jsternberg Looks good

@jsternberg jsternberg force-pushed the command-duration-metric branch 3 times, most recently from 555c4eb to 3d93cd6 Compare February 2, 2024 19:50
build/utils.go Outdated
@@ -34,7 +34,7 @@ func IsRemoteURL(c string) bool {
return false
}

func isLocalDir(c string) bool {
func IsLocalDir(c string) bool {
Copy link
Member

Choose a reason for hiding this comment

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

Maybe move this to osutil?

Comment on lines 201 to 206
func ToAbs(path string) string {
if !filepath.IsAbs(path) {
path, _ = filepath.Abs(filepath.Join(osutil.GetWd(), path))
}
return SanitizePath(path)
}
Copy link
Member

Choose a reason for hiding this comment

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

Could be moved to osutil as well but needs to move SanitizePath there too, can be for a follow-up.

func getVCSPath(contextPath string) string {
var wd string
if filepath.IsAbs(contextPath) {
wd = contextPath
Copy link
Member

@crazy-max crazy-max Feb 6, 2024

Choose a reason for hiding this comment

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

contextPath in here can be - or a URL.

Hum right this was never checked when getting Git attributes.

func getGitRemoteURL(contextPath string) string {
// Check if this is a valid path to begin with before attempting
// to run git. It might be a non-filesystem URL.
if _, err := os.Stat(contextPath); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

@jsternberg Looks good

@jsternberg
Copy link
Collaborator Author

This PR is now also semi-dependent on this PR here: docker/cli#4875

At the current moment, we don't have a way of obtaining the alias that was used to launch buildx. That PR sets an environment variable so the plugin can find the original command name so we can include it in the metric.

@jsternberg jsternberg force-pushed the command-duration-metric branch from a60f069 to 8f88d0e Compare February 14, 2024 20:59
@jsternberg
Copy link
Collaborator Author

I've removed the section of this that's dependent on docker/cli. That PR seems like it's likely going to come to either a different method of passing the data or setting something like OTEL_SERVICE_NAME and including this information as part of the service name.

I don't want to prevent this PR from being merged while we hash that out so I've removed it.

@jsternberg jsternberg force-pushed the command-duration-metric branch from 8f88d0e to c42265f Compare February 14, 2024 21:03
Copy link
Member

@tonistiigi tonistiigi left a comment

Choose a reason for hiding this comment

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

@jsternberg Check the windows CI

This adds a build duration metric for the build command with attributes
related to the buildx driver, the error type (if any), and which options
were used to perform the build from a subset of the options.

This also refactors some of the utility methods used by the git tool to
determine filepaths into its own separate package so they can be reused
in another place.

Also adds a test to ensure the resource is initialized correctly and
doesn't error. The otel handler logging message is suppressed on buildx
invocations so we never see the error if there's a problem with the
schema url. It's so easy to mess up the schema url when upgrading OTEL
that we need a proper test to make sure we haven't broken the
functionality.

Signed-off-by: Jonathan A. Sternberg <[email protected]>
@jsternberg jsternberg force-pushed the command-duration-metric branch from c42265f to bda968a Compare February 14, 2024 21:58
@jsternberg
Copy link
Collaborator Author

Was missing an import on the Windows build because my setup doesn't auto perform imports for systems that aren't compiled on my platform (aka windows in this case). Fixed it by adding the correct import. I'll keep an eye out because some stuff got moved around and that commonly means imports get removed and readded in new places so hopefully there's no more missing areas.

@tonistiigi tonistiigi merged commit c9d1c41 into docker:master Feb 14, 2024
63 checks passed
@jsternberg jsternberg deleted the command-duration-metric branch February 14, 2024 23:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants