add download method for updater#20
add download method for updater#20groob merged 11 commits intokolide:masterfrom groob:download_method
Conversation
| return &Client{manager: manager}, nil | ||
| } | ||
|
|
||
| func (c *Client) Update() (files map[targetNameType]FileIntegrityMeta, latest bool, err error) { |
There was a problem hiding this comment.
// update refreshes local state, returning all updated targets.
| return <-ff, latest, nil | ||
| } | ||
|
|
||
| func (c *Client) Download(targetName string, destination io.Writer) error { |
There was a problem hiding this comment.
Download securely copies a target to an io.Writer, doing all the checks on that file.
| } | ||
| currentMeta, ok := files[targetNameType(targetName)] | ||
| if !ok { | ||
| // return errors.Errorf("targetName %s not found", targetName) |
| return nil | ||
| } | ||
|
|
||
| func (fim FileIntegrityMeta) verifyIO(r io.Reader, size int64) error { |
There was a problem hiding this comment.
This method works like fim.verify() but attempts to work on a stream in stead of []byte, addressing #18
| } | ||
|
|
||
| func (fim FileIntegrityMeta) verifyIO(r io.Reader, size int64) error { | ||
| if size != int64(fim.Length) { |
There was a problem hiding this comment.
@murphybytes I had to convert int to int64 a few times. Would it make sense to just set fim.Length to an int64 type, so it's decoded from JSON into int64? Would probably be easier to work with.
There was a problem hiding this comment.
Yes. I don't think that will cause any issues?
| targets *Targets | ||
| client *http.Client | ||
|
|
||
| actionc chan func() |
There was a problem hiding this comment.
the Snapshot, Timestamp, Targets and Root fields on this structure need to be safe for concurrency. To accomplish that, I created a function type channel which I use in the new refresh method, avoiding race conditions and ensuring files are updated/read in order.
| func (rs *repoMan) refresSafe() (bool, error) { | ||
| errc := make(chan error) | ||
| var isLatest bool | ||
| rs.actionc <- func() { |
There was a problem hiding this comment.
example action channel function.
| <-quit | ||
| } | ||
|
|
||
| func (rs *repoMan) refresSafe() (bool, error) { |
There was a problem hiding this comment.
need to remove the old refresh() method and rename this one to refresh.
| return stagePath, nil | ||
| } | ||
|
|
||
| func (rs *repoMan) downloatTargetToWriter() error { |
| } | ||
|
|
||
| func (rs *repoMan) refreshTargets(root *Root, snapshot *Snapshot) (*Targets, string, error) { | ||
| func (rs *repoMan) refreshTargets(root *Root, snapshot *Snapshot) (*Targets, bool, error) { |
There was a problem hiding this comment.
In the previous method, refreshTargets downloaded the settings.Target.
Sometimes we want to refresh without downloading to the staging path. I let the caller decide if they need to update.
There's a problem here, that someone calling Download() will update the local repository, but that won't be seen by the looping update function... need to think/test better.
|
My general sense is that you're adding a lot of complexity here by adding concurrency and I don't see the need for it given what our needs are today. Concurrency will make it more probable that we'll miss a test case for a situation that might cause a panic in a production environment. If you have a really really good reason for going down this path I'd like to hear it. |
|
I'm not adding concurrency, just trying to limit the potential for race conditions in already existing situations. Today there's a loop which checks for new updates every X minutes. That loop ends up calling That same method is also needed in order to |
|
I see your point. Another way to do this would be to add a Download method to the updater, that uses a channel to send a message to the goroutine in the updater, this would perform the download, blocking refresh until you're done, at which point you send a message back to the Download method with the results. Or you could send a function that would get executed in the updater goroutine and get the results as arguments. |
|
If you could perform the operations in the updater goroutine you eliminate the possibility of race conditions since that goroutine is the only thing that touches the tuf repository |
|
That doesn't fix a problem that exists right now: the update method cares about a specific target, like But the intention of the Download method is to download a specifc target, instead of the one in the settings. Say the updater is watching for There needs to be a way to watch for updates to a specific file rather than to the entire targets file.. |
|
what I'm saying is that the current updater method assumes that if the targets.json has been updated there was an update to a specific target, which is not true. |
|
Ahh, got it. I see what you're up to. |
| actionc chan func() | ||
| checkFrequency time.Duration | ||
| notificationHandler NotificationHandler | ||
| client *tuf.Client |
There was a problem hiding this comment.
Maybe the client would be better if it were instantiated inside the loop and was not a member of the Updater struct since your sharing it's state between the main process and the loop goroutine unless you have a reason to share that state.
There was a problem hiding this comment.
If you instantiated the client in the goroutine you could pass it to an argument the func you send over the channel.
There was a problem hiding this comment.
I need to call download on the client itself so it has to be available on the updater struct.
| select { | ||
| case <-ticker: | ||
| case f := <-u.actionc: | ||
| f() |
| // goroutine. | ||
| errc := make(chan error) | ||
| u.actionc <- func() { | ||
| _, _, err := u.client.Update() |
There was a problem hiding this comment.
u.actionc <- func(client *tuf.Client) {
_, _, err := client.Update()
if err != nil {
errc <- err
return
}
errc <- client.Download(target, destination)
return <-errc
}
There was a problem hiding this comment.
That would limit the action channel to only be usable with a func(client) signature. I don't see the benefit. We'll just have to change it next time we want to do something in the loop.
| @@ -124,21 +128,72 @@ func (u *Updater) Stop() { | |||
| func (u *Updater) loop() { | |||
| ticker := time.NewTicker(u.checkFrequency).C | |||
There was a problem hiding this comment.
So you'd instantiate the tuf.Client here.
|
@murphybytes this branch is now ready to merge. I've updated the tests and verified manually that the example works with both continually monitoring a target and with using Download directly. Once this is merged, I'll work on #22 where I'll only expose the current updater methods and make everything except the types private. |
Closes #13
In the process of adding the method also discovered and updated a few other concerns:
Closes #16
Closes #18
still a WIP pr for now