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

Make start and stop functions blocking #181

Merged
merged 26 commits into from
Jun 5, 2018
Merged

Conversation

NicolasMahe
Copy link
Member

@NicolasMahe NicolasMahe commented Jun 1, 2018

  • Add WaitForContainerStatus func
    It's using a time.Sleep to block the thread. Should I start a go routine that will sleep and on the main block with a waitgroup? I think the time.Sleep is enough

  • Update Daemon start

  • Update Daemon stop

  • Update daemon commands

  • Update Service start
    It's using a WaitGroup to wait for all dependencies to start.
    It's also using a mutex to edit serviceIDs array that is shared between the goroutines.

  • Update Service stop
    It's using a WaitGroup to wait for all dependencies to stop.

  • Update service commands

@NicolasMahe NicolasMahe self-assigned this Jun 1, 2018
@codecov
Copy link

codecov bot commented Jun 1, 2018

Codecov Report

Merging #181 into dev will increase coverage by 0.35%.
The diff coverage is 75.36%.

Impacted file tree graph

@@            Coverage Diff             @@
##              dev     #181      +/-   ##
==========================================
+ Coverage   69.16%   69.51%   +0.35%     
==========================================
  Files          58       58              
  Lines        1216     1263      +47     
==========================================
+ Hits          841      878      +37     
- Misses        303      309       +6     
- Partials       72       76       +4
Impacted Files Coverage Δ
container/service_options.go 100% <100%> (ø) ⬆️
daemon/stop.go 55.55% <100%> (+5.55%) ⬆️
daemon/start.go 66.66% <42.85%> (-5.56%) ⬇️
container/container.go 79.48% <80%> (+0.17%) ⬆️
service/start.go 77.41% <80.95%> (+2.41%) ⬆️
service/stop.go 75.75% <83.33%> (+4.32%) ⬆️
api/core/stop.go 66.66% <0%> (-4.77%) ⬇️
api/service/emit_event.go 100% <0%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0005c7c...10be2d1. Read the comment docs.

for {
currentStatus, err := ContainerStatus(namespace)
if err != nil {
return err
Copy link
Member

Choose a reason for hiding this comment

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

I would use some break here in order to make sure that we always exit the for loop, it's a bit easier to test

Copy link
Member Author

@NicolasMahe NicolasMahe Jun 4, 2018

Choose a reason for hiding this comment

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

Because of the new context of the for and the creation of currentStatus, err is also created. So if I use break, I'm pretty sure the err will not be assign to the return err.
If I write, the following code, go says err is shadowed during return on the return.

for {
    currentStatus, err := ContainerStatus(namespace)
    if err != nil {
	return

That's why I think if I use the break, the error will also be shadowed.

So there are 2 solutions.
The first one is to use return err as it's already the case.
The second is to created currentStatus and then assign the value:

var currentStatus StatusType
currentStatus, err = ContainerStatus(namespace)
if err != nil {
	break
}

@antho1404 What solution do you think it's the best?

Copy link
Member

Choose a reason for hiding this comment

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

I personally use the second solution all the time, we should definitely avoid shadowed variable it's the kind of things that can create bugs easily.

@@ -71,7 +74,12 @@ func (options *ServiceOptions) swarmPorts() (ports []swarm.PortConfig) {
return
}

func (options *ServiceOptions) swarmMounts() (mounts []mount.Mount) {
func (options *ServiceOptions) swarmMounts(force bool) (mounts []mount.Mount) {
// hack for preventing mount when in CircleCI
Copy link
Member

Choose a reason for hiding this comment

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

A bit hacky, I would add a comment TOFIX just to make sure that we can find this again when the CI allow to mount directories

Copy link
Member Author

Choose a reason for hiding this comment

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

Comment updated

@@ -8,3 +12,13 @@ const (
STOPPED StatusType = 1
RUNNING StatusType = 2
)

// TimeoutError represents an error of timeout
Copy link
Member

Choose a reason for hiding this comment

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

I think this is not a really good way to have errors I like the fact that we have the details in the error message but i'm sure we can do it better, I would just use something like that directly

errors.New(Timeout reached after " + e.duration.String() + " for ressource " + e.name)

or maybe have a function

func timeoutError(duration, resource) error {
  return errors.New(Timeout reached after " + e.duration.String() + " for ressource " + e.name)
}

or just a constant with generic error message but like that we can compare this error in the tests for example.

const TimeoutError = errors.New("Timeout error on container action")

This is a bit too much and confusing because you don't even return an error

Copy link
Member Author

Choose a reason for hiding this comment

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

It's the classic way to create new errors in go and be able to compare. Eg with type casting:

_, ok := err.(*TimeoutError)

https://blog.golang.org/error-handling-and-go

On the contrary, I think we should start creating custom error than using the default one with just a custom description. It will be way easier to debug.

Copy link
Member Author

@NicolasMahe NicolasMahe Jun 4, 2018

Choose a reason for hiding this comment

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

In the case of the TimeoutError, we can imagine than the CLI could decide to do a retry and then wait again.

Copy link
Member

Choose a reason for hiding this comment

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

it just feels like a lot of code just for a simple error that could be done in one constant but ok, let's follow the guidelines

service/start.go Outdated
wg.Add(1)
go func(service *Service, d dependencyDetails, name string, i int) {
serviceID, errStart := dependency.Start(service, d, networkID)
mutex.Lock()
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure a mutex is needed here because in this code we have different goroutine that access different index so there is no reason to have conflicts but still is not something bad to do

service/start.go Outdated
if err != nil {
return
}
err = container.WaitForContainerStatus(namespace, container.RUNNING, time.Minute) //TODO: be careful with timeout
Copy link
Member

Choose a reason for hiding this comment

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

a minute is really long. If it was in the cli it might be ok but for an internal function I think it's definitely too long

Copy link
Member Author

Choose a reason for hiding this comment

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

What timeout you think is ok?

Otherwise, we could pass the timeout duration as a parameter of the Start and Stop function.

service/stop.go Outdated
if errStop != nil && err == nil {
err = errStop
}
mutex.Unlock()
Copy link
Member

Choose a reason for hiding this comment

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

same remark here about using defer

Copy link
Member

Choose a reason for hiding this comment

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

also here i don't see why there is a mutex, in the map it makes sense because it's not thread safe if two threads want to write on the same index but with the error I don't think there is any problems

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated to use defer.

Copy link
Member Author

Choose a reason for hiding this comment

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

The reason for the mutex is the err comparison in the if:

if .... err == nil {

service/stop.go Outdated
return
}
err = container.StopService([]string{namespace, dependencyName})
err = container.WaitForContainerStatus(namespace, container.STOPPED, time.Minute) //TODO: be careful with timeout
Copy link
Member

Choose a reason for hiding this comment

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

again probably too long one minute

@antho1404
Copy link
Member

There is no update on the changelog we should add a line on how this will change the user experience

@NicolasMahe
Copy link
Member Author

Changelog updated.
Please review my comments of your comments.

@antho1404
Copy link
Member

still have some errors from codeclimate

@NicolasMahe
Copy link
Member Author

I'm merging even with the codeclimate issues. I don't know how to fix it in a simple way..

@NicolasMahe NicolasMahe merged commit 20fbb11 into dev Jun 5, 2018
@NicolasMahe NicolasMahe deleted the blocking-start-stop branch June 5, 2018 06:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants