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 ArrayEncodedMap #2750

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Conversation

carolynvs
Copy link
Member

@carolynvs carolynvs commented Apr 26, 2023

What does this change

It's a common pattern in Porter to represent a map of items, with a unique key, as a list in the porter.yaml or a file representation of the resource because the UX for autocomplete is better. We can't change that now, parameters and credentials for example will always be a list, but we can use a map in Porter's code so that it's easier to work with.

I've added ArrayEncodedMap, a data structure that reads in a list and stores it in-memory as a map:

  • name: a
    value: stuff
  • name: b
    value: things

We can use this as a base data structure initially for Parameter and Credential Sets, storing the mapping of the parameter/credential name to the secret resolution strategy. Over time, it may make sense to use it for the other data structures in porter.yaml as well.

It has support for iterating as a map, or as a sorted list of values. The sorted list is good for unit tests, and anytime we need the output to be stable. The map isn't exposed directly and I have a funky "best you can do with Go" iterator to help keep our for loops looking pretty normal.

What issue does it fix

Refactoring work for PEP003 as I found working with parameter sets as maps made parameter resolution much more straightforward.

Notes for the reviewer

This is a replacement for my original PR #2734, which was adding helper methods to work with the set of values by key, but was still storing the values in a slice.

I will follow-up with another PR after this is merged to use it in ParameterSet/CredentialSet. You can see what that looks like in #2749. I wanted to give us a chance to bikeshed this first so that it's not super hard to rework with suggestions. Once we hook it up to ps/cs, the changeset gets pretty large.

Alternative names to consider... 😁

  • MessedUpMap
  • ArrayMap (Aaron said this means something in other languages, so probably best to avoid)
  • A nonsense name like Blinko, so people don't waste time worrying about what the name implies about the implementation
  • NotAMap

Checklist

  • Did you write tests?
  • Did you write documentation?
  • Did you change porter.yaml or a storage document record? Update the corresponding schema file.
  • If this is your first pull request, please add your name to the bottom of our Contributors list. Thank you for making Porter better! 🙇‍♀️

Reviewer Checklist

  • Comment with /azp run test-porter-release if a magefile or build script was modified
  • Comment with /azp run porter-integration if it's a non-trivial PR

It's a common pattern in Porter to represent a map of items, with a unique key, as a list in the porter.yaml or a file representation of the resource because the UX for autocomplete is better. We can't change that now, parameters and credentials for example will always be a list, but we can use a map in Porter's code so that it's easier to work with.

I've added ArrayEncodedMap, a data structure that reads in a list and stores it in-memory as a map:

- name: a
  value: stuff
- name: b
  value: things

We can use this as a base data structure initially for Parameter and Credential Sets, storing the mapping of the parameter/credential name to the secret resolution strategy. Over time, it may make sense to use it for the other data structures in porter.yaml as well.

It has support for iterating as a map, or as a sorted list of values. The sorted list is good for unit tests, and anytime we need the output to be stable. The map isn't exposed directly and I have a funky "best you can do with Go" iterator to help keep our for loops looking pretty normal.

Signed-off-by: Carolyn Van Slyck <me@carolynvanslyck.com>
@carolynvs carolynvs added the pep003-advanced-dependencies Implementation of the Advanced Dependencies proposal label Apr 26, 2023
@carolynvs carolynvs marked this pull request as ready for review April 27, 2023 12:26
Copy link
Contributor

@arschles arschles left a comment

Choose a reason for hiding this comment

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

Looks good overall. I left a few questions

// Use ItemsUnsafe() to directly manipulate the backing items map.
func (m *ArrayEncodedMap[T, K]) Items() map[string]T {
if m == nil {
return nil
Copy link
Contributor

Choose a reason for hiding this comment

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

return an empty map here, so callers don't blow up?

Copy link
Contributor

Choose a reason for hiding this comment

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

Is this even reachable? If the object is nil then referencing any method on the object should result in a panic before it hits the implementation correct?

Copy link
Contributor

Choose a reason for hiding this comment

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

Never mind just clarified my own question. Go can call methods on nil structs :)

return nil
}

if m.items == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

is it ever possible to have items be nil?

// UnmarshalRaw is the common Marshal implementation between YAML and JSON.
func (m *ArrayEncodedMap[T, K]) UnmarshalRaw(raw []K) error {
if m == nil {
*m = ArrayEncodedMap[T, K]{}
Copy link
Contributor

Choose a reason for hiding this comment

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

won't this panic? seems like you'd need to set the value of the pointer to valid memory

Suggested change
*m = ArrayEncodedMap[T, K]{}
m = &ArrayEncodedMap[T, K]{}


// UnmarshalYAML unmarshals the items in the specified YAML.
func (m *ArrayEncodedMap[T, K]) UnmarshalYAML(value *yaml.Node) error {
var raw []K
Copy link
Contributor

Choose a reason for hiding this comment

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

same here RE checking m == nil

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pep003-advanced-dependencies Implementation of the Advanced Dependencies proposal
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants