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 Annotation parsing and Config from Annotations functions #359

Merged
merged 10 commits into from
Sep 27, 2021

Conversation

john-odonnell
Copy link
Contributor

@john-odonnell john-odonnell commented Sep 2, 2021

What does this PR do?

This PR allows for configuring Secrets Provider with Pod annotations. Annotation settings are prioritized over environment variable settings, but envvars can still be used to configure SP to maintain backwards compatibility.

Adds annotations_parser.go and annotation_parser_test.go to package secrets/annotations, including:

  • AnnotationsFromFile(path string) (map[string]string, error)
  • ParseAnnotations(annotationsFile io.Reader) (map[string]string, error)
  • Test Cases
    • A valid annotations file is parsed successfully
    • Malformed annotations file lines result in an error indicating the line number and desired format

Adds functions to secrets/config package:

  • ValidateAnnotations(annotations map[string]string) ([]error, []error)
    • Confirms annotation keys & value types, and confirms value for those annotations with enumerated value options
    • Returns lists of Error-level and Info-level logs
    • Test Cases
      • Properly formatted annotations return empty arrays
      • Confirm Info log if for unrecognized annotations with and without conjur.org/ prefix
      • Confirm Error log for annotations given improper value type
      • Confirm Error log for annotations with explicit input ranges given invalid values
  • GatherSecretsProviderSettings(annotations map[string]string) map[string]string
    • Returns an map containing annotations and envvars required from Secrets Provider config
    • Test Cases
      • If the annotations map is empty, the resulting map contains envvars used in SP config
      • If envvars are not set, the resulting map contains contains the annotations used in SP config
      • If both are present, the resulting map is all envvars and annotations used in SP config
      • Annotations and envvars not used in SP config are excluded
  • ValidateSecretsProviderSettings(envAndAnnots map[string]string) ([]error, []error)
    • Confirm that the provided annotations and envvars lend to a valid SP config
    • Input validation on annotation keys and values
    • Returns Error and Info level log messages. An empty Error array means validation has succeeded.
    • Test Cases
      • Given a valid configuration of annotations, no errors are returned
      • Given a valid configuration of envvars, no errors are returned
      • Confirm that MY_POD_NAMESPACE is required in either case
      • Confirm Error log if StoreType is not set
      • Confirm Error log is Push-to-File is being configured with envvars
      • Confirm Error log if K8s Secrets mode is missing required K8s secrets
      • Confirm Info log if values are provided to equivalent annotation and envvar
  • NewConfig(settings map[string]string) *Config
    • Create a new SP config from a validated map of annotations and SP Config envvars
    • Test Cases
      • A valid map of annotation-based settings results in a valid Config
      • A valid map of envvar-based settings results in a valid Config
      • A valid map of both annotation and envvar settings defers to annotation values

New functions are used in SP main function to create a new SP Config

Acceptance Criteria Review

  • An annotation parser exists for extracting arbitrary annotations from a downward API annotation file. This should include a test case to validate this behavior.
  • Annotations can be used instead of environment variables. This should include a test case to validate this behavior.
  • Annotations take precedence over environment variables. This should include a test case to validate this behavior.
  • Unknown Conjur annotation causes INFO level log
  • Multiline annotation values are parsed correctly

What ticket does this PR close?

ONYX-11827
Closes #330

Checklists

Change log

  • The CHANGELOG has been updated, or
  • This PR does not include user-facing changes and doesn't require a CHANGELOG update

Test coverage

  • This PR includes new unit and integration tests to go with the code changes, or
  • The changes in this PR do not require tests

Documentation

  • Docs (e.g. READMEs) were updated in this PR, and/or there is a follow-on issue to update docs, or
  • This PR does not require updating any documentation

@john-odonnell john-odonnell changed the title Add Annotation Parser to utils package Add Annotation Parser package Sep 9, 2021
@john-odonnell john-odonnell force-pushed the ONYX-11827 branch 3 times, most recently from efbcf5a to 6a3a84c Compare September 13, 2021 16:40
@john-odonnell john-odonnell changed the title Add Annotation Parser package Add Annotation parsing and Config from Annotations functions Sep 14, 2021
@john-odonnell john-odonnell marked this pull request as ready for review September 14, 2021 15:12
@john-odonnell john-odonnell requested review from a team as code owners September 14, 2021 15:12
Includes Annotation Parser, custom error and info messages, and unit tests.
- Convert tests from Convey to vanilla Go unit testing, for
  both Annotations and Config packages
- Refactor ParseAnnotations function to remove unit test
  dependency on file system operations
- Move annotation key and value validation to config package.
  The annotations package should only parse - validation is
  the responsibility of the consuming entity.
- Consolidate NewFromEnv, NewFromAnnotations, and MergeConfig
  into one function, NewConfig. Helps solve problems that arise
  from handling Configs compiled from different sources that
  are dependent on each other.
- Update unit tests to reflect these changes.
@john-odonnell john-odonnell force-pushed the ONYX-11827 branch 2 times, most recently from 859523a to 379477f Compare September 23, 2021 19:44
Validate annotation key-values and
Secrets Provider config separately
Copy link
Contributor

@diverdane diverdane left a comment

Choose a reason for hiding this comment

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

Really nice job weaving all of the different pieces of the configuration together (SP env variables, authn-k8s env variables, Annotations)! The resulting flow is pretty easy to follow, and it looks like you've covered all of the edge cases.

Nice work on the table-driven test cases and the use of closure for the assertion funcs!

I just have minor comments and some just-for-grins color commentary. Otherwise looks good-to-go.

pkg/secrets/annotations/annotation_parser.go Outdated Show resolved Hide resolved
cmd/secrets-provider/main.go Outdated Show resolved Hide resolved
printErrorAndExit(messages.CSPFK049E)
}

secretsProviderSettings := secretsConfigProvider.GatherSecretsProviderSettings(annotationsMap)
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice work on fitting together all of the config puzzle pieces here.

"github.com/cyberark/secrets-provider-for-k8s/pkg/log/messages"
)

func AnnotationsFromFile(path string) (map[string]string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Exported names should have a preceding doc comment that begins with the function name and briefly describes what it does, e.g.:

// AnnotationsFromFile reads and parses an annotations file that has been created by Kubernetes
// via the Kubernetes Downward API, based on Pod Annotations that are defined in a deployment
// manifest.

I surprised we're not seeing errors for missing doc comments for exported names.... maybe we need to add runs of go lint in our tests? I think my version of Vim automatically flags this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Noted, and gave all exported functions proper comments.
Is there a go-to linter for Go projects? I looked at golint, but it looks like it's been deprecated for a while.

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like revive is pretty standard, and is comparable to what golint was.
I installed and ran revive manually with:

go install github.com/mgechev/revive@latest
revive -formatter friendly

When I removed the doc comment that you just added and ran revive, I see:

$ revive -formatter friendly
  ⚠  https://revive.run/r#exported  exported function AnnotationsFromFile should have comment or be unexported  
  annotation_parser.go:14:1

  ⚠  https://revive.run/r#exported  func name will be used as annotations.AnnotationsFromFile by other packages, and that stutters; consider calling this FromFile  
  annotation_parser.go:14:6

⚠ 2 problems (0 errors, 2 warnings)

Warnings:
  2  exported  

$

I'll have to see if I can add this to my Go-Vim config (and/or Goland).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ran revive on the files I changed, and had to update some constants in config.go that propagated to a couple other files. Had to convert from THIS_CASE to ThisCase.

return ParseAnnotations(annotationsFile)
}

// List and multi-line annotations are formatted as a single string in the annotations file,
Copy link
Contributor

Choose a reason for hiding this comment

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

This comment really helps!

pkg/secrets/config/config.go Outdated Show resolved Hide resolved
return fmt.Errorf(messages.CSPFK042E, key, value, "Integer")
}
case "bool":
if value != "true" && value != "false" {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can a boolean Annotation value be set to all-caps "TRUE" or "FALSE"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, not with this implementation. If we want, I can use strconv.ParseBool to determine if a boolean setting was acceptable, which would allow "TRUE" and "FALSE", and some others:

ParseBool returns the boolean value represented by the string. It accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False. Any other value returns an error.

pkg/secrets/config/config_test.go Outdated Show resolved Hide resolved
pkg/secrets/config/config_test.go Outdated Show resolved Hide resolved
}

func TestValidateAnnotations(t *testing.T) {
for _, tc := range validateAnnotationsTestCases {
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 COMPLETELY a personal preference sort of thing, but in the past, I've created table-driven tests where all of these are defined in one test function:

  • The test case struct (this struct can be anonymous, if it's included in an instantiation of this struct, i.e. the test list)
  • The test case list (e.g. validateAnnotationsTestCases)
  • The iteration/running through all of the test cases

The benefit to this is that all definitions required for the test are gathered into one spot (one function). The downside is that the test function ends up with a LOT of lines (less visual breaks).

@john-odonnell john-odonnell force-pushed the ONYX-11827 branch 2 times, most recently from a1c298c to 1026bf3 Compare September 24, 2021 22:20
@codeclimate
Copy link

codeclimate bot commented Sep 24, 2021

Code Climate has analyzed commit 1026bf3 and detected 0 issues on this pull request.

The test coverage on the diff in this pull request is 95.2% (50% is the threshold).

This pull request will bring the total coverage in the repository to 93.0% (5.5% change).

View more on Code Climate.

Copy link
Contributor

@diverdane diverdane left a comment

Choose a reason for hiding this comment

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

LGTM!!!

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.

Secrets Provider Configuration via annotations M1
2 participants