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

feat(jfrog): support multiple repositories #289

Merged
merged 11 commits into from
Sep 27, 2024

Conversation

BrentSouza
Copy link
Contributor

This MR addresses two items:

  1. There is a bug in run.sh that keeps adding autocompletion to either .bashrc or .zshrc
  2. Allow users to specify multiple repositories for a given artifact type

Autocompletion

The run.sh script is looking for the wrong string to check if autocompletion has already been applied. The changes here correct that so autocompletion stanzas are only added once to the relevant shell startup script.

Multiple Repos

With Artifactory, you can have multiple repositories of a given type. For example, at my company, we promote docker images across environment specific repositories to keep our production image repository clean. It would be nice to setup auth for each of these instead of picking just one. The changes here allow for a list of repos to be defined. The first will function as a global repository, with varying behavior for the others based on the type.

pypi

The first repo is added to pip.conf as index-url, while any additional repos are added to extra-index-url

npm

The first repo is added as a default registry. Subsequent repos should be scoped, e.g. @foo:npm-repo-name and they will be added as such to .npmrc

docker

Attempts to authenticate to all defined repositories

go

Adds all repos to GOPROXY

I've added some tests to check the various output in the generated script, but I still need to validate that the module works as intended in our coder deployment. I wanted to open this pull request now to get any potential feedback on the changes. If this is ok, I'll also extend the changes to the jfrog-oauth module.

@matifali matifali self-requested a review September 16, 2024 18:06
@matifali matifali changed the title feat(modules): JFrog Module Enhancements feat(jfrog-token): JFrog Toke Module Enhancements Sep 17, 2024
@BrentSouza BrentSouza changed the title feat(jfrog-token): JFrog Toke Module Enhancements feat(jfrog-token): JFrog Token Module Enhancements Sep 17, 2024
@BrentSouza BrentSouza marked this pull request as ready for review September 17, 2024 11:14
@BrentSouza
Copy link
Contributor Author

Tested the module from my branch in our deployment and it's working at least how I intended! Please let me know if there are any changes you would like made.

@matifali
Copy link
Member

Thank you, @BrentSouza, for the contribution. We have two modules for JFrog; one uses OAuth, and the other uses an admin token to create user-scoped access tokens. Would it be possible for you to port all these changes to the OAuth module, too?
Again this is a great addition.

matifali
matifali previously approved these changes Sep 17, 2024
@BrentSouza
Copy link
Contributor Author

Thanks @matifali! Yes I will happily make the changes to jfrog-oauth. Do you want a separate PR for that one?

@matifali
Copy link
Member

Let's keep them in a single PR.

@matifali matifali changed the title feat(jfrog-token): JFrog Token Module Enhancements feat(jfrog): support multiple repositories Sep 18, 2024
@matifali
Copy link
Member

matifali commented Sep 19, 2024

Once you are done with the changes. I can test both of them and merge the PR.
Again thanks a lot for the contribution.

@BrentSouza
Copy link
Contributor Author

BrentSouza commented Sep 20, 2024

@matifali at your convenience, please take a look at the port to jfrog oauth 😄. I can't test this one locally as we are on coder community edition and already have external auth configured with our GitLab instance.

@BrentSouza
Copy link
Contributor Author

Actually I found a bug already 😱 Patch incoming

@BrentSouza
Copy link
Contributor Author

Ok @matifali I think we should have successful test and pretty runs now

@matifali
Copy link
Member

matifali commented Sep 20, 2024

@BrentSouza Thank you for the contribution. This is a huge enhancement.

@matifali matifali dismissed their stale review September 20, 2024 14:57

Need to test and re-review

Copy link
Contributor

@Parkreiner Parkreiner left a comment

Choose a reason for hiding this comment

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

Checked all the test files, and while I had some feedback, they all look good

@matifali I'm going to defer to you on the Terraform files. I think they look good, but you definitely know more than I do. As long as you think they look good, I think this is pretty safe to approve

@@ -17,15 +17,16 @@ Install the JF CLI and authenticate package managers with Artifactory using OAut
```tf
module "jfrog" {
source = "registry.coder.com/modules/jfrog-oauth/coder"
version = "1.0.15"
version = "2.0.0"
Copy link
Contributor

Choose a reason for hiding this comment

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

@matifali I'm still not fully clear on how we're handling versioning, but we would want this to be version 1.0.19?

Copy link
Member

Choose a reason for hiding this comment

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

yes, we will go with 1.0.19 for now until we resolve #157.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok. I was bumping the major version since the input variable format for the repos is a breaking change, but if there's an outside constraint, I'll update the readme to reflect the target version of 1.0.19

Copy link
Member

Choose a reason for hiding this comment

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

It is indeed a breaking change but we do not want to bump all future modules to 2.0.0 before we do #157.

const fakeFrogUrl = `http://${fakeFrogHostAndPort}`;

it("can run apply with required variables", async () => {
testRequiredVariables(import.meta.dir, {
Copy link
Contributor

Choose a reason for hiding this comment

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

Just to provide a little more type-safety, could you define a custom type for the variables, and pass it along as a type parameter?

type TestVariables = Readonly<{
  agent_id: string;
  jfrog_url: string;
  package_managers: string;

  username_field?: string; 
  jfrog_server_id?: string;
  external_auth_id: string;
  configure_code_server?: boolean;
}>;

You'd then be able to pass this type to both runTerraformApply and testRequiredVariables to catch any accidental typos:

runTerraformApply<TestVariables>(/* Same arguments as before */)

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 appreciate the concept here but please allow me to push back slightly on this suggestion. I fear it might be redundant to define the variables in the test with minimal value because:

  1. This is not at all in the runtime path, we're just potentially saving a developer testing time
  2. Typos will bubble up as a result of running the mock terraform apply and the tests will fail
  3. Since there is no definitive binding between the variables as written in tf to the variables in this type def, there's nothing to prevent someone from typing the variable name incorrectly in the typescript type def.

In summary, I think you are only validating the test code is type safe and not really validating the logic of the module.

I don't see this pattern in many, if any, of the other tests for modules but if you are adamant that this is the correct thing for your code, I will implement it.

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 put this in place in the interest of getting these changes merged. I do think it feels contrived since you have to define the type and then make sure you specify the type as a type param for the various function calls to get the benefit of enforcing the type check.

It might be a tighter workflow if you had a generic Testing class instead of the free functions in test.ts. So you would do something like:

describe("some-module", async () => {
  type SomeVariables = {
    ...
  };
  const test = new Testing<SomeVariables>();
  const state = await test.runTerraformApply(...);
  ...
});

Obviously a change outside the scope of this PR, but does that make sense to you?

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, was a little swamped these past few days, and haven't had a chance to respond earlier

On one hand, I guess I view the current setup as not that different from other JavaScript values like Maps or Sets. If you have something like this:

const userMap = Map<string, User>();

I don't think that's too different from one of the testing functions. The type restrictions don't exist at runtime, so non-user values can still accidentally be placed in the Map.

But I think you're right, and I think the current type behavior is a bit of a bandaid. These are just test files, so them breaking on typos isn't as huge of a deal, because you get instant feedback before they bubble up to an end-user

But going to your idea about the generic testing class, that does seem like a really good idea. The reason why having Map<string, User> is valuable is because the type restriction trickles down to each individual method. You don't have to keep repeating the type info on every single call

Copy link
Contributor

Choose a reason for hiding this comment

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

I think I'll actually make a separate PR once this one is done to leverage classes more

Comment on lines 104 to 105
const proxies = `https://default:@${fakeFrogHostAndPort}/artifactory/api/go/foo,https://default:@${fakeFrogHostAndPort}/artifactory/api/go/bar,https://default:@${fakeFrogHostAndPort}/artifactory/api/go/baz`;
expect(proxyEnv["value"]).toEqual(proxies);
Copy link
Contributor

@Parkreiner Parkreiner Sep 20, 2024

Choose a reason for hiding this comment

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

I feel like this might be a little more readable:

Suggested change
const proxies = `https://default:@${fakeFrogHostAndPort}/artifactory/api/go/foo,https://default:@${fakeFrogHostAndPort}/artifactory/api/go/bar,https://default:@${fakeFrogHostAndPort}/artifactory/api/go/baz`;
expect(proxyEnv["value"]).toEqual(proxies);
const proxies = [
`https://default:@${fakeFrogHostAndPort}/artifactory/api/go/foo`,
`https://default:@${fakeFrogHostAndPort}/artifactory/api/go/bar`,
`https://default:@${fakeFrogHostAndPort}/artifactory/api/go/baz`
];
expect (proxyEnv.value).toBe(proxies.join(","));

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How about this:

["foo","bar","baz"].map(r => `https://default:@${fakeFrogHostAndPort}/artifactory/api/go/${r}`).join(",");

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, that works, too

Comment on lines +81 to +83
package_managers: JSON.stringify({
docker: ["foo.jfrog.io", "bar.jfrog.io", "baz.jfrog.io"],
}),
Copy link
Contributor

Choose a reason for hiding this comment

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

This definitely isn't your fault, but I'm making a note so I don't forget: we should update our helper functions so that they support Terraform objects directly

const fakeFrogUrl = `http://${fakeFrogHostAndPort}`;

it("can run apply with required variables", async () => {
testRequiredVariables(import.meta.dir, {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment as the auth test file. Looks like the type can be set up like this?

type TestVariables = Readonly<{
  agent_id: string;
  jfrog_url: string;
  artifactory_access_token: string;

  jfrog_server_id?: string;
  token_description?: string;
  check_license?: boolean;
  refreshable?: boolean;
  expires_in?: number;
  configure_code_server?: boolean;
  package_managers?: string;
}>;

Comment on lines 134 to 135
const proxies = `https://default:xxx@${fakeFrogHostAndPort}/artifactory/api/go/foo,https://default:xxx@${fakeFrogHostAndPort}/artifactory/api/go/bar,https://default:xxx@${fakeFrogHostAndPort}/artifactory/api/go/baz`;
expect(proxyEnv["value"]).toEqual(proxies);
Copy link
Contributor

Choose a reason for hiding this comment

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

Also feel like this could be split up for readability

- Fix versions in readme
- Make expected test values readable
- Support typed vars for testing
@BrentSouza
Copy link
Contributor Author

I've addressed all concerns in the latest commit. I had to make some changes to test.ts to support typed vars since the method signature for testRequiredVariables was Record<string, string>

@matifali matifali requested a review from Parkreiner September 23, 2024 08:36
Copy link
Contributor

@Parkreiner Parkreiner left a comment

Choose a reason for hiding this comment

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

Thank for being so patient with me. The tests look good, and also, I think your suggestion for how to update the general test approach makes a lot more sense than what we have right now

@BrentSouza
Copy link
Contributor Author

No thanks necessary. Thanks for the taking the time to give valuable feedback!

@matifali matifali merged commit ad1189a into coder:main Sep 27, 2024
2 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