Skip to content

Conversation

@avilevy18
Copy link
Contributor

@avilevy18 avilevy18 commented Jul 22, 2025

Updating distrogen to add full project generation. It assumes that there will always be one distribution per project.

@avilevy18 avilevy18 requested a review from braydonk July 24, 2025 20:33
@avilevy18 avilevy18 requested a review from braydonk July 29, 2025 19:25
Copy link
Contributor

@braydonk braydonk left a comment

Choose a reason for hiding this comment

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

I ran a manual test and most things I tried seemed to work. I encountered one bug, which is that both a distrogen and .distrogen folder were generated.

I think it would be a good idea to put together project generation golden test cases, similar to what we have in testdata/generator, which are defined in distribution_test.go. You could also add full project generation test cases to check those goldens. There is certainly more we could do for testing, but I think at least having that would be a good start (and would catch the bug I encountered).

After that, the scratch folder can be removed and we can formally move this to in review.

}

for _, tmpl := range templates {
logger.Debug("context of template", "ctx", tmpl.Context)
Copy link
Contributor

Choose a reason for hiding this comment

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

We can probably get rid of these debug logs.

@avilevy18 avilevy18 requested a review from braydonk August 5, 2025 15:06
@jefferbrecht
Copy link
Member

Please add a motivation to the PR description. What problem is this solving? It doesn't look like a trivial change and the uninformed reader is not going to understand what's happening here.

Copy link
Contributor

@ridwanmsharif ridwanmsharif left a comment

Choose a reason for hiding this comment

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

Please add a motivation to the PR description. What problem is this solving? It doesn't look like a trivial change and the uninformed reader is not going to understand what's happening here.

+1 to Jeffs comment, I got pretty lost in this deep dark forest so consider this a partial review for now


var ErrComponentNotFound = errors.New("component not found")

type ComponentType string
Copy link
Contributor

Choose a reason for hiding this comment

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

[nit] Why does this have to be of type string and not an enum?

Copy link
Contributor

Choose a reason for hiding this comment

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

It's more convenient to use it as a key to the registry when we're editing it based on the component type we're trying to add.

type ComponentType string

const (
Receiver ComponentType = "receiver"
Copy link
Contributor

Choose a reason for hiding this comment

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

Interesting that we are adding support for all these types. Receivers I do get, but do we have any examples of why we think the others are useful too?

I'm trying to convince myself of a use case where we would want to do this? Is this motivated by the processors that the Ops Agent uses?

Copy link
Contributor

Choose a reason for hiding this comment

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

Most of this PR was designed for the general use case, not just for us. There's no logical reason to lock down what types of custom components are allowed in a registry that I can think of.

)

type ProjectGenerator struct {
Spec *DistributionSpec
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are all these exported? They seem to only be used within this package?

Copy link
Contributor

Choose a reason for hiding this comment

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

I have been keeping the door open for restructuring the project; right now it's still a flat folder but it's possible it might become a cmd folder and then a separate package for the whole distrogen API. I haven't had any time to make a sweeping change like that but don't want to close the door on it either (it would be more annoying if these were all unexported and then had to all be renamed when it gets moved).


type ProjectGenerator struct {
Spec *DistributionSpec
FileMode fs.FileMode
Copy link
Contributor

Choose a reason for hiding this comment

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

When is this set to something other than 0777? If we don't set it anything else, we don't need to be part of this struct, or part of the Get.*TemplateSet signatures.

Copy link
Contributor

Choose a reason for hiding this comment

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

There was a bit of dev history with this one. When developing on a system with a particular umask set (which it actually is on our internal development machines) the files could be generated with permissions that caused building in Dockerfiles to not work. As a result I had to add the filemode to any os API calls. At first I used a global default variable for that, and opted to make it part of the structs so that if stuff got split up we wouldn't have to add it later.

We could make a choice that we don't need to ever change it and always refer to the global default. At the time I was messing with a number of different values but gave up and ended on 0777 just to make it work and have since forgotten about it.

if err := os.MkdirAll(filepath.Join(generatePath, "templates"), pg.FileMode); err != nil {
dirErrors = append(dirErrors, err)
}
if _, err := os.Create(filepath.Join(generatePath, "templates", EMPTY_FILE_NAME)); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wondering why we create this file here manually but below we rely on the templates to create .distrogen/VERSION

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 had an issue with go:embed where it couldn't find nested hidden files so it couldn't find the .distrogen folder. Now that I have fixed that will multiple go:embed this is no longer needed. I removed the manual creation of the .distrogen folder.

Name: componentName,
}

switch componentType {
Copy link
Contributor

Choose a reason for hiding this comment

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

making this an enum like I suggested above should get rid of this switch statement with all fallthroughs

Copy link
Contributor

Choose a reason for hiding this comment

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

If it was an enum we'd just move the switch case from here to where we use it to index the registry instead, idk which one is better seems like the same level of complexity

@@ -0,0 +1,4 @@
module {{ .Spec.Module }}

go {{ .Spec.RenderGoMajorVersion }}
Copy link
Contributor

Choose a reason for hiding this comment

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

Feels weird that we let people specify the go version but then only use the major version here.

I think typically in the go.mod file you can use the patch version too. And failing that you can also use the toolchain of the patch version you want. Like https://github.com/jaegertracing/jaeger/blob/fa9a5e7d31fcc1163b6bbed741dc288146871959/go.mod#L3-L5

Not saying we need to do this, but was wondering why we do what we do now

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

@jefferbrecht jefferbrecht Sep 8, 2025

Choose a reason for hiding this comment

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

Whether you include the patch or not is orthogonal to whether it's a collector or not. Sometimes in Go you need to specify a patch version if your code relies on some quirk of the language/toolchain that's only available after a certain patch level.

You can choose to outright disallow patch levels if you really think it's a pitfall, but if that's the case, do it loudly by raising an error instead of silently truncating it.

(Unfortunate that Go itself doesn't follow semver here... I would've said that RenderGoMajorVersion should actually be named RenderGoMajorAndMinorVersion, but no, that's actually what Go refers to as a major version...)

Copy link
Contributor

Choose a reason for hiding this comment

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

This is a convention of the project (and in fact we got ourselves into trouble in the Collector for specifying the patch version of Go in our operations-go modules). If you specify the version it makes Go's automatic toolchain resolution do some automatic downloading of the version that some random module asked for. It might be fine for our case but I was just thinking we should follow the convention of upstream on this.

Copy link
Member

@jefferbrecht jefferbrecht Sep 8, 2025

Choose a reason for hiding this comment

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

(doesn't need to block this PR, this is more of a general wondering)

Are there any docs explaining the need for a component registry?

If I'm wearing my component author hat, what kind of indexing does the registry provide that the built-in module/package management system in Go doesn't provide already? I'm already able to broadcast my component to the world with the tooling already available.

If I'm wearing my distro author hat, I can already identify components directly by their URI. What is the registry's indirection providing me?

We talked a bit offline about the challenges of working with components that aren't available from a proper GOPROXY or aren't available publicly, but the toolchain has e.g. GOPRIVATE for that.

Copy link
Contributor

Choose a reason for hiding this comment

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

This will be part of the more general distrogen breakdown doc I am working on, but I'll answer briefly here.

Registry gives us the following things currently:

  • Allows us to use a logical name for a component rather than the whole URL in the spec.yaml
  • Allows redirecting a module to a local package or import path, basically propagating the options available in OCB

It will enable the following unreleased things:

  • Declaring a supported OTel Collector version. This looks fine right now cause we only refer to upstream, who tag the components based on the Collector release, but if anyone decides to tag things in ways that don't match upstream, this becomes confusing. We want distrogen to reconcile and complain about components it resolved that don't match the requested collector version.
  • Propagating necessary Go replaces. Rather than needing to go out to a component's source repo and checking if they have any replaces that they need to function properly, distrogen will be able to recognize that it's pulled a component from a registry that is requiring certain module replacements and replicate them in the OCB manifest.

Deeper explanation will be part of the design document I'm working on for the latter two.

Copy link
Contributor

@ridwanmsharif ridwanmsharif left a comment

Choose a reason for hiding this comment

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

I'll let the two of you resolve the open threads, I think I'm okay with most of the answers given as is - we can iterate on the rest in follow up changes

# Conflicts:
#	cmd/distrogen/distribution.go
#	cmd/distrogen/testdata/generator/distribution/build_container/golden/spec.yaml
@avilevy18 avilevy18 marked this pull request as ready for review September 23, 2025 20:04
@avilevy18 avilevy18 merged commit c1d5f61 into master Sep 24, 2025
10 checks passed
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.

4 participants