-
Notifications
You must be signed in to change notification settings - Fork 24
Conversation
|
||
Spec ResourceSpec `json:"spec"` | ||
} | ||
|
||
// +k8s:deepcopy-gen=true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this needed given that there is a method for it below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say the other way around. Is there a reason for manually defining a DeepCopyInto
method, if it should be generated based on this annotation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's what's happening here.
for i := range *in { | ||
(*in)[i].DeepCopyInto(&(*out)[i]) | ||
} | ||
copy(*out, *in) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ARGHH #216
pkg/controller/resource_sync_task.go
Outdated
if !st.processedResources[dependency].isReady() { | ||
notReadyDependencies = append(notReadyDependencies, dependency) | ||
// No len here because dependencies can occur more than once in reference list | ||
notReadyDependenciesSet := make(map[smith_v1.ResourceName]bool) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The convention is to use struct{}
as value for sets. This makes it more explicit that you don't care about the value.
pkg/controller/spec_processor.go
Outdated
) | ||
|
||
var ( | ||
reference = regexp.MustCompile(`^\{\{.+}}$`) | ||
reference = regexp.MustCompile(`(?s)^(!+)\{(.+)}$`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the reader:
https://github.com/google/re2/wiki/Syntax
(?s)
- let . match \n (default false)
pkg/controller/spec_processor.go
Outdated
func NewExamplesSpec(references []smith_v1.Reference) (*SpecProcessor, error) { | ||
variables, err := resolveAllReferences(references, func(reference smith_v1.Reference) (interface{}, error) { | ||
if reference.Example == nil { | ||
return nil, errors.WithStack(&noExampleError{reference.Name}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer specifying field names explicitly even if there is only one field. If you later refactor - add a field of the same type - this will not break the compilation but become a bug silently.
pkg/controller/spec_processor.go
Outdated
for _, reference := range references { | ||
// Don't 'resolve' nameless references - they're just being | ||
// used to cause dependencies. | ||
if reference.Name != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd unindent the happy path and rewrite this as if reference.Name == "" { continue }
pkg/controller/spec_processor.go
Outdated
smith_v1 "github.com/atlassian/smith/pkg/apis/smith/v1" | ||
"github.com/atlassian/smith/pkg/resources" | ||
"github.com/pkg/errors" | ||
"go.uber.org/multierr" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we use some multierr package from client-go somewhere already. Lets pick one =)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't find it...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
k8s.io/apimachinery/pkg/util/errors/NewAggregate()
pkg/controller/spec_processor.go
Outdated
if err != nil { | ||
return nil, errors.Wrapf(err, "invalid reference at \"%s\"", strings.Join(path, ".")) | ||
// This means we have multiple '!' at the start, so we're escaping. | ||
if len(match[1]) > 1 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should ignore pairs of !
and then process the rest if there is an unmatched !
, right? This can even be done with regex - match 0+ pairs and then expect an !
. Hm, but then those pairs need to be transformed into single !
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also please add tests for escaping. And we should export a function to escape a string and another one to escape a whole object. Also maybe add a flag to each resource to tell smith whether to look for references in this resource or not. Because the choice for programmatic construction is between 1) escape each one even if it does not contain references by construction 2) set a flag marking the resource as not containing references. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree re:tests. I would like to see examples of such escaping format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I thought I added tests.
I don't see a problem with this method of escaping though - it should just work, aside from the programmatic construction case... my preference here, rather than a flag, would be to look for references IFF there are references (i.e. if sp.variables is non-empty).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(yes, it's a little magical, but it keeps the base case clean)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On further thought, I think the current behaviour is the correct one. Programmatic users should just have to deal with escaping (it's relatively straightforward, as are the error cases... we also have no cases currently).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will add the functions to escape strings/objects in a separate PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall I like the direction of this PR.
See the comments.
|
||
Spec ResourceSpec `json:"spec"` | ||
} | ||
|
||
// +k8s:deepcopy-gen=true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say the other way around. Is there a reason for manually defining a DeepCopyInto
method, if it should be generated based on this annotation?
for _, dependency := range res.DependsOn { | ||
if !st.processedResources[dependency].isReady() { | ||
notReadyDependencies = append(notReadyDependencies, dependency) | ||
// No len here because dependencies can occur more than once in reference list |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is a justification for not passing an initial capacity at all.
you can easily set more key-value pairs than initially allocated, i.e. using len(res.References)
is actually fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
? But then I over-allocate, potentially dramatically?
(it's not under-allocation that will happen, it's over-allocation)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah I see. I doubt there is a huge memory footprint for map[string]struct{}
.
And also I don't see any reason for a dependency occuring more than once in reference list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(it will occur more than once because of the path problem)
pkg/controller/resource_sync_task.go
Outdated
} | ||
} | ||
notReadyDependencies := make([]smith_v1.ResourceName, 0, len(notReadyDependenciesSet)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can allocate a fixed size array instead here:
notReadyDependencies := make([]smith_v1.ResourceName, len(notReadyDependenciesSet))
i := 0
for resourceName, _ := range notReadyDependenciesSet {
notReadyDependencies[i] = resourceName
i++
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this equivalent, just more verbose and error prone?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(maybe append is slightly slower? You'd think it would be optimised away. But I'm using the same array...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think append is inherently safer and reads nicer =)
pkg/controller/resource_sync_task.go
Outdated
} | ||
} | ||
notReadyDependencies := make([]smith_v1.ResourceName, 0, len(notReadyDependenciesSet)) | ||
for resourceName, _ := range notReadyDependenciesSet { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_
is redundant here. Golang magic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heh, I find it ugly to leave out. Ok, will change.
@@ -69,8 +69,10 @@ func TestWorkflow(t *testing.T) { | |||
Spec: smith_v1.BundleSpec{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(for myself and other reviewers)
equivalent in YAML:
spec:
resources:
- name: config1res
references:
- resource: secret2res
spec:
object: ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wryun if I understand correctly, such minimal reference is only suitable for creation ordering, and not for injecting values from the other objects right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. You need to specify name/path if you want an actual value.
pkg/controller/spec_processor.go
Outdated
ar := make(map[smith_v1.ReferenceName]interface{}, len(references)) | ||
var finalerr error | ||
for _, reference := range references { | ||
// Don't 'resolve' nameless references - they're just being |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if I like the idea of this behavior depending on whether name
is defined or not. See comments above about name
vs resource
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather remove name
or make it optional.
And we can resolve all references without checking for name != ""
, this optimization seems redundant to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what you mean here. Maybe talk in person.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TL;DR just remove this block:
// used to cause dependencies.
if reference.Name == "" {
continue
}
and I propose to delete the name
field completely.
pkg/controller/spec_processor.go
Outdated
if err != nil { | ||
return nil, errors.Wrapf(err, "invalid reference at \"%s\"", strings.Join(path, ".")) | ||
// This means we have multiple '!' at the start, so we're escaping. | ||
if len(match[1]) > 1 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree re:tests. I would like to see examples of such escaping format.
pkg/controller/spec_processor.go
Outdated
} | ||
if fieldValue == nil { | ||
return nil, errors.Errorf("field not found: %s", selector) | ||
return nil, errors.Errorf("field not found: %s", reference.Path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
%q
{ | ||
Name: "res1aslice", | ||
Resource: "res1", | ||
Path: "a.slice[?(@.label==\"label2\")].value", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we are not even referring to name
inside the path or anywhere else, right? What's the purpose of name
then?
obj := map[string]interface{}{ | ||
"ref": map[string]interface{}{ | ||
"slice": "{{res1#a.slice[?(@.label==\"label2\")].value}}", | ||
"string": "{{res1#a.string}}", | ||
"slice": "!{res1aslice}", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok I see, we use name
there.
@nilebox take a look at the proposal here #233 (comment) - this should clarify what is going on in this PR. |
@wryun would be great to also update all the documentation to use the new syntax introduced in this PR. |
@wryun To keep this PR smaller I suggest to remove escaping functionality from it. And then add it in a later PR. We need to discuss it first, I have a feeling it might require more code than we have here. |
c749934
to
1bc5334
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some minor comments from me which I mostly addressed myself 😛
// Refer to a part of another object | ||
type Reference struct { | ||
Name ReferenceName `json:"name,omitempty"` | ||
Resource ResourceName `json:"resource"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be ResourceName ResourceName
. But then it is a bit too verbose. WDYT?
pkg/controller/spec_processor.go
Outdated
|
||
resolvedRef, err := resolveReference(reference) | ||
if err != nil { | ||
finalerr = multierr.Append(finalerr, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this by design the there is no continue
here to avoid adding invalid resolved ref into the map?
The remaining work here is to fix the docs. |
I'm going to merge this because I reeeally want to start tinkering with the "plugable controller" idea 😛 |
Ah, sorry. I had intended to get to it today, but house painting makes me lazy (actually, most things make me lazy). |
See #233 for some discussion. DO NOT MERGE - need to update doco if we agree. Also, integration-test may be broken (unclear what's happening on first glance).
Why we want this:
Why we don't want this: