Deliver secrets using assignments#1544
Conversation
56d6c73 to
08b109f
Compare
|
|
||
| // Secrets is a map that keeps all the currenty available secrets to the agent | ||
| type Secrets struct { | ||
| sync.RWMutex |
There was a problem hiding this comment.
It's a good idea to unembed the mutex as a private field so callers in other packages can't directly lock or unlock it.
mu sync.RWMutexOr even better, unexport the Secrets type if it will only be used in this package.
There was a problem hiding this comment.
Have done both in the latest changes.
| db *bolt.DB | ||
| executor exec.Executor | ||
| listeners map[*statusReporterKey]struct{} | ||
| secrets *Secrets |
There was a problem hiding this comment.
Not sure this should be a pointer
There was a problem hiding this comment.
If we added the Get/Add/Remove apis to secrets, seems like they should have pointer receivers because they're mutating internal state. In that case, should this be a pointer in order to call those APIs?
There was a problem hiding this comment.
This doesn't have to be referenced through a pointer from the worker struct for its methods to have pointer receivers.
There was a problem hiding this comment.
Oh cool - I thought it had to be a pointer in order to call functions that had pointer receivers. Thanks
There was a problem hiding this comment.
Changed to be not a pointer
| if fullSnapshot { | ||
| w.secrets.m = make(map[string]*api.Secret) | ||
| for _, secret := range added { | ||
| w.secrets.m[secret.Spec.Annotations.Name] = secret |
There was a problem hiding this comment.
Modifying the map with the read side of the lock held looks like a mistake.
There was a problem hiding this comment.
Changed these to call CRD apis on secret instead, which calls mu.Lock instead when adding or removing or resetting.
Current coverage is 54.90% (diff: 89.47%)@@ master #1544 diff @@
==========================================
Files 83 85 +2
Lines 13989 14120 +131
Methods 0 0
Messages 0 0
Branches 0 0
==========================================
+ Hits 7519 7752 +233
+ Misses 5456 5350 -106
- Partials 1014 1018 +4
|
stevvooe
left a comment
There was a problem hiding this comment.
Where is this injected into the container?
| ) | ||
|
|
||
| // Secrets is a map that keeps all the currenty available secrets to the agent | ||
| type Secrets struct { |
There was a problem hiding this comment.
That contains just this secret struct with its APIs, or also all the worker apis that also deal with secrets?
There was a problem hiding this comment.
Added a secrets.go under the agent package that contains the secret structure, along with some additional APIs that manipulate the map.
| if fullSnapshot { | ||
| w.secrets.m = make(map[string]*api.Secret) | ||
| for _, secret := range added { | ||
| w.secrets.m[secret.Spec.Annotations.Name] = secret |
There was a problem hiding this comment.
Let's add an api to the secrets object to avoid manipulate it's locks directly.
| }, | ||
| indexName: { | ||
| Name: indexName, | ||
| Unique: true, |
There was a problem hiding this comment.
This says secret names must be unique, but I think we decided against that.
There was a problem hiding this comment.
Oh good point, thanks I missed this one.
c00303f to
d2028a9
Compare
diogomonica
left a comment
There was a problem hiding this comment.
LGTM, but I think no Spec changes should be made in this PR.
| } | ||
|
|
||
| // SecretSpec specifies a user-provided secret. | ||
| message SecretSpec { |
There was a problem hiding this comment.
Should the SecretSpec be here? Maybe this PR should be rebased out of #1511 so it doesn't include these portions?
| } | ||
|
|
||
| // SecretReference is the linkage between a service and a secret that it uses. | ||
| message SecretReference { |
There was a problem hiding this comment.
Same thing here. SecretReference should have been define in #1511
|
@aluzzardi Ah apologies, could you please review #1567 first? We probably should merge some of the changes from there (to the secret state store), and IIRC this one may need some changes to the way assignments are done as per comments in #1511. cc @diogomonica |
d2028a9 to
c92dbc1
Compare
Add a Secret top-level object type. Add a SecretReference that allows a service to reference the secrets it needs. Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
Signed-off-by: Diogo Monica <diogo.monica@gmail.com>
…ipulating secrets. Also update some of the object protos to make things easier to look up. Signed-off-by: cyli <cyli@twistedmatrix.com>
…ets and max number of secret versions. Also add dispatcher protos for secrets. Signed-off-by: cyli <cyli@twistedmatrix.com>
Signed-off-by: cyli <cyli@twistedmatrix.com>
Assignments now provides a stream with incremental task and secret updates. Additional object types can be supported in the assignment set in the future. The first message returned from the Assignments stream is the complete set of tasks and secrets for the node, and this is used to synchronize the node's view with the manager's. Additional messages returned by the stream are incremental updates that add, update, or remove one or more tasks or secrets. If the agent gets out of sync with the manager, it can reinitiate the Assignments stream to sync up. Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
Signed-off-by: Diogo Monica <diogo.monica@gmail.com>
Signed-off-by: Diogo Monica <diogo.monica@gmail.com>
Signed-off-by: Diogo Monica <diogo.monica@gmail.com>
Signed-off-by: Diogo Monica <diogo.monica@gmail.com>
…nt from secret state store. Signed-off-by: cyli <cyli@twistedmatrix.com>
c92dbc1 to
3787807
Compare
…t Changes Signed-off-by: Diogo Monica <diogo.monica@gmail.com>
5f9222a to
a9e0e46
Compare
|
|
||
| for _, secretRef := range container.Secrets { | ||
| secretID := secretRef.SecretID | ||
| if tasksUsingSecret[secretID] == nil { |
There was a problem hiding this comment.
I think this check isn't necessary, since deleting an item from a nil map does not panic. https://play.golang.org/p/e3GXoOb6SC
The check below it should cover the case of adding removeSecrets[secretID] = struct{}{} and updating modified.
Will remove this check.
There was a problem hiding this comment.
Confirmed in the spec:
If the map m is nil or the element m[k] does not exist, delete is a no-op.
a9e0e46 to
7d2f5d6
Compare
| Checks: []state.TaskCheckFunc{state.TaskCheckNodeID}}, | ||
| state.EventDeleteTask{Task: &api.Task{NodeID: nodeID}, | ||
| Checks: []state.TaskCheckFunc{state.TaskCheckNodeID}}, | ||
| state.EventUpdateSecret{}, |
There was a problem hiding this comment.
Non-blocking question: does this part works without providing it a secret? Can you watch just all generic events this way? I tried deleting a secret that was being used in a task in TestAssignments, but nothing happened (no changes were pushed). I'm not familiar with the dispatcher though, so my expectations might just be wrong.
There was a problem hiding this comment.
Having state.EventUpdateSecret{} should receive all updates to any secret.
I tried deleting a secret that was being used in a task in TestAssignments, but nothing happened (no changes were pushed)
That sounds like a bug to me. Could you try debugging the code under case state.EventDeleteSecret: (seeing if it triggers, what it does, etc...)?
There was a problem hiding this comment.
Ah you're right, sorry, it was looking up the secrets by name, thanks. :) Fixed and uncommented test.
There was a problem hiding this comment.
After discussion with @diogomonica, since we probably don't want to delete secrets out from under a task, going to make this a noop for now.
|
Also non-blocking question: looks like |
…re sent down. Also, if a Secret corresponding to a SecretReference doesn't exist, there is no error. Signed-off-by: cyli <ying.li@docker.com>
7d2f5d6 to
86453e2
Compare
Signed-off-by: Diogo Monica <diogo.monica@gmail.com>
|
|
|
||
| func reconcileSecrets(ctx context.Context, w *worker, assignments []*api.AssignmentChange, fullSnapshot bool) error { | ||
| var ( | ||
| updatedSecrets []api.Secret |
There was a problem hiding this comment.
Why does this use []api.Secrets as opposed to reconcileTaskState which uses []*api.Task? Not saying it's a problem, but wondering if there's a reason for storing pointers in one case and copies of the object in another.
There was a problem hiding this comment.
I'm guessing it's because the secrets map object takes copies of secrets. I can change that to take pointers to secrets instead, but it seems like the golang wiki encourages you to pass (small) structs by value unless you're modifying it. Secrets seem small to me (we aren't modifying them) but I don't really know at what point they'd consider a struct "large" :)
I also don't really have any strong feelings on the matter and I'd be happy to go with whatever the preferred convention is.
There was a problem hiding this comment.
Updated the tasks in reconcileTaskState to be a list of tasks rather than pointers to tasks.
| message Assignment { | ||
| oneof item { | ||
| Task task = 2; | ||
| Secret secret = 3; |
There was a problem hiding this comment.
It seems a bit strange to remove a secret by sending down a copy of the secret. Not sure I understand how this works. In the old code, we removed a secret by sending the ID of the secret to remove, which makes more sense to me.
There was a problem hiding this comment.
I'm not sure about this one. :) Can this be addressed in a future PR? Seems like then an AssignmentChange should be one of AssignmentUpdateChange (which has an Assignment field) or AssignmentRemoveChange (which has a string field representing IDs). Not sure if in the future we'd want to be able to remove by more than just ID, and that change might limit that ability.
There was a problem hiding this comment.
We do this to avoid creating another enum type for each message type. The packed object works similar to a tagged enum, without extra pageantry.
| removedSecrets []string | ||
| ) | ||
| for _, a := range assignments { | ||
| if s, ok := a.Assignment.GetItem().(*api.Assignment_Secret); ok { |
There was a problem hiding this comment.
a.Assignment.GetSecret()
| removedTasks []*api.Task | ||
| ) | ||
| for _, a := range assignments { | ||
| if t, ok := a.Assignment.GetItem().(*api.Assignment_Task); ok { |
There was a problem hiding this comment.
if t := a.Assignment.GetTask(); t != nil
| if equality.TasksEqualStable(oldTask, v.Task) && v.Task.Status.State > api.TaskStateAssigned { | ||
| // this update should not trigger a task change for the agent | ||
| tasksMap[v.Task.ID] = v.Task | ||
| // If this task go updated to a final state, lets release |
Signed-off-by: cyli <ying.li@docker.com>
4e81e86 to
9491db8
Compare
|
On Thu, Oct 06, 2016 at 02:38:33PM -0700, Ying Li wrote:
I agree with that in general. Don't let this block the PR. Ideally I |
2280581 to
2a24ac8
Compare
Signed-off-by: cyli <ying.li@docker.com>
2a24ac8 to
cb2eb0a
Compare
Since it's a protobuf change, I think we should get it right here. ping @diogomonica: Any thoughts? |
|
|
||
| message Assignment { | ||
| oneof item { | ||
| Task task = 2; |
There was a problem hiding this comment.
This should be field number 1.
Depends on #1511 (as in, this is stacked on top of the changes in #1511)
This adds secrets to the raft store, and implements dispatching of secrets.