Skip to content

Refactor#799

Closed
krhubert wants to merge 49 commits intodevfrom
service-refactor
Closed

Refactor#799
krhubert wants to merge 49 commits intodevfrom
service-refactor

Conversation

@krhubert
Copy link
Copy Markdown
Contributor

@krhubert krhubert commented Mar 7, 2019

No description provided.

@krhubert krhubert force-pushed the service-refactor branch 3 times, most recently from 2dd3260 to 1f8c203 Compare March 7, 2019 19:50
@antho1404
Copy link
Copy Markdown
Member

I had a quick look, I have few feedback for now:

  • API package should accept the service as parameter for the stop, start etc... instead of the sid/hash like that we can api.StartService(api.GetService()) this will be easier to delegate to a system service

  • Why reverting to events/task using map and not array

  • I like the service manager but why not even delegating this directly to the API to simplify

@krhubert krhubert mentioned this pull request Mar 8, 2019
@krhubert krhubert force-pushed the service-refactor branch 2 times, most recently from f72dd7b to b9e1cb6 Compare March 8, 2019 06:25
@krhubert
Copy link
Copy Markdown
Contributor Author

krhubert commented Mar 8, 2019

API package should accept the service as parameter for the stop, start etc... instead of the sid/hash like that we can api.StartService(api.GetService()) this will be easier to delegate to a system service

If API accepts service then who manage databases to get them?
grpc server should manage communication layer only.

The flow right now is:
grpc -> api -> service -> container

If you want to accept service struct in api package then we need something between grpc and ap

I like the service manager but why not even delegating this directly to the API to simplify

To avoid api package becoming bigger. And I felt that manager struct belongs more to service (as it only operates on service). Moving this to api means also that api imports container package so it measn it depends on service and container package rather then service itself.

Why reverting to events/task using map and not array

From discussion on discord we end up with:

The array keeps the order or parameters and it's more deterministic definition of service so the array is preferable way here. The second thing is that definitions and protobuf should be same if possible, so if we have array we will have them in mesg.yaml as well (@antho1404 please confim that what I wrote cover our discussion or add missing part)

@ilgooz
Copy link
Copy Markdown
Contributor

ilgooz commented Mar 8, 2019

This PR is too big, it needs to be split to smaller ones with explanatory commit messages.

@antho1404
Copy link
Copy Markdown
Member

The array keeps the order or parameters and it's more deterministic definition of service so the array is preferable way here. The second thing is that definitions and protobuf should be same if possible, so if we have array we will have them in mesg.yaml as well

Not exactly.

  • Keeping the array structure because more precise 👍
  • definition and protobuf similar is a nice to have
  • mesg.yml is a representation so don't need to have arrays, this is oversimplification that we can do if we want too but this impacts a lot existing services and reduces a lot the readability of the yaml so i suggest to keep the parsing of the yaml and convert it in the data structure with array except if we all agree to move with array only on the yaml

@krhubert krhubert force-pushed the service-refactor branch 2 times, most recently from d898cd3 to 2bf586d Compare March 12, 2019 17:14
Comment thread core/main.go
Comment thread database/service_db.go
Comment thread execution/execution.go Outdated
NicolasMahe
NicolasMahe previously approved these changes Mar 19, 2019
Copy link
Copy Markdown
Member

@NicolasMahe NicolasMahe left a comment

Choose a reason for hiding this comment

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

Manually tested 👍

@NicolasMahe
Copy link
Copy Markdown
Member

@antho1404 @ilgooz please review.

Copy link
Copy Markdown
Member

@antho1404 antho1404 left a comment

Choose a reason for hiding this comment

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

few feedback but I didn't do a full validation yet and no testing yet.
It will take quite some time to test correctly everything. Again I strongly encourage to split this PR as many things are independent

Comment thread Dockerfile
Comment thread api/deploy.go
return nil, err
}
defer resp.Body.Close()
if err := archive.Untar(resp.Body, contextDir, nil); err != nil {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why changing back, why not keep using the DeployService that takes a reader and will simplify the function and also put all the logic in the same place if we need to do some extra work for the tar

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

we wanted service to accept contextDir as arguments. So there are few cases here:

  • api receives io.Reader that could be read only once
  • api needs to formalizeContext (remove .git etc...) so it needs to extract the tar
  • service needs a directory to calculate the hash
  • service needs tar.Reader to pass it to docker deploy api

So combining all this either service will receive io.reader, extract tar, calc the hash and then archive it back, or the other way.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

that's not my point here, why not use DeployService(resp.Body, ...) and have a pseudo function like that

if isGit {
  dir = createTmp()
  gitClone(arg, dir)
  return deploy(dir)
}
resp = http.Get(arg)
return DeployService(resp.Body, ...)

Here we have only one function responsible for the untar, not 2 and nothing else changes and we still have the context

Copy link
Copy Markdown
Contributor Author

@krhubert krhubert Mar 21, 2019

Choose a reason for hiding this comment

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

To send status about Service downloaded. Also it seems more readable to me.

return fmt.Sprintf(`%s Dockerfile exists
%s mesg.yml is valid
%s Service is valid`, pretty.SuccessSign, pretty.SuccessSign, pretty.SuccessSign), nil
return fmt.Sprintf(`%s mesg.yml is valid`, pretty.SuccessSign), nil
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Agree with that argument but we still need to validate that the service contains a dockerfile

return string(content), err
}

func taskByKey(s *coreapi.Service, key string) *coreapi.Task {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

should return an error if not found to avoid risk of null pointer

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

we can but we can check for the null as well, it's just a helper function only used inside this file.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

just seems better to adopt this pattern all the time. Also we are returning the error anyway just after so let's get the error from the function itself

Copy link
Copy Markdown
Member

@NicolasMahe NicolasMahe Mar 21, 2019

Choose a reason for hiding this comment

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

I feel it's not a really good pattern in Go to return a pointer without error.
I speak about this with Hubert about should we keep pointer in the data structure of service or not. We didn't find a good solution.
In both TypeScript and in Swift, they force to check if the variable / pointer is nil / undefined before accessing a value in it. This is not something that the Go compiler enforce. I feel it opens the place for many nil pointer error...

What about removing all pointers in the struct of the service? Like this, for example, this function taskByKey, will need to have the following signature: taskByKey(s coreapi.Service, key string) (coreapi.Task, error).
It will consume more memory, but we are forced to follow a good pattern.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

the pointer discussion is something else, the problem will be the same. Either we test something based on the value returned or based on an error.
After I kind of agree if it's pure data we don't necessary need pointers as long as it's not huge data and not really important that are present everywhere in all the modules. For this one I don't mind keeping the pointers. I don't see any really good reason to change it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

we can change to error, it's really small change (but error won't ever happen here) but it's ok

Comment thread database/service_db.go
}
if err := tx.Delete([]byte(hashKeyPrefix+s.Hash), nil); err != nil {
return err
if s.Sid != "" {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sid should never be "" in the database. Services without Sid should be rejected as it is a mandatory data (at least if we have volumes)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So they are not mandatory right now (they are marked as optional).

Copy link
Copy Markdown
Member

@antho1404 antho1404 Mar 21, 2019

Choose a reason for hiding this comment

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

What about volumes ? 2 services with no sid will share the same volume.
Right now they are not mandatory in the mesg.yml but generated with a value by default (_HASH if i remember correctly) so the definition file is optional but in the core mandatory.

This change related to Sid breaks volumes for services

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In service manager, if sid is empty it uses the hash.
The behavior is exactly like before.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

great I didn't see that. Also behavior is not exactly, we are taking the hash, before we where appending an character in front of the hash to make sure that we cannot create a service with the hash of another one as sid and then overwrite the previous service/volume. But anyway that was not a strong security anyway.

I still feel more confident to make sure that we can rely on a service to have a sid (that is generate when we deploy it like the hash) instead of having to check all the time we want to use the sid. I'm not necessary against it either it's just something I would like to have agreement to see the pro and con on that and not change something that impact the data.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

In that case, we have to generate different sid because one we generated one is an invalid domain. Also, sid should be used only for local development and should not be mandatory.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

sid is the base for the markeplace too so it's not only about local development. We put sid as optional with generation in order not to break previous services but every services needs to have a sid to identify this service and "group" the different version to the same service.

How do you manage services without sid on the marketplace for example ?

Comment thread event/event.go
return nil, err
}
// New creates an event eventKey with eventData for given service.
func New(s *service.Service, eventKey string, eventData map[string]interface{}) *Event {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Any good reason to move the validation from this package ? It ensure consistency in the data. Agree that it doesn't impact the data but in that case maybe the data should change and instead have a reference of the event maybe.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It was moved to api package (so api handles all validation) and in that case event and execution packages depend only on service structure (and not the way how to validate them). So both package can take care only of it's logic (like update complete status for execution)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I saw that this was moved on the API but what happen if we use the event/execution packages directly without the API ? We might introduce invalid data ? Also this doesn't reduce any dependencies as event/execution packages already depends on the service package.

Comment thread execution/execution.go
if err := task.RequireInputs(inputs); err != nil {
return nil, err
}
func New(service *service.Service, eventID string, taskKey string, inputs map[string]interface{}, tags []string) *Execution {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same here

Comment thread service/manager.go Outdated
string image = 1; // Image's name of the container.
repeated string volumes = 2; // List of volumes.
repeated string volumesfrom = 3; // List of volumes mounted from other dependencies.
repeated string volumesFrom = 3; // List of volumes mounted from other dependencies.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this is breaking change, not critical and i'm ok with it but just that everyone acknowledge this

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It's fine here. The mesg.yml still have the volumesfrom.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

And no, it's not a breaking change, gRPC uses the number (in this case 3) to decode the data. So this is compatible with previous version.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We are changing the data so it is a breaking change if any client like cli for example are reading this property which is quite unlikely but still this is a breaking change

Comment thread service/manager.go
}

// Delete see Manager.Delete.
func (m *ContainerManager) Delete(service *Service) error {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe DeleteVolumes. This doesn't delete the service so that's confusing

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So this is the implementation of the Manager interface

  // Delete deletes all persistent data used by service.
  Delete(service *Service) error

So for Container, it happens to delete volumes but for other deploy methods, it could be something else.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Instead of having the stop and delete logic in API, it could be done at the service manager.
Stop is just stopping the service.
Delete: stop + delete persistent data.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No, it's manager part to have deleted and stop function separated. so api (or any other packge) can use them, and there they should be splited.

ilgooz added a commit that referenced this pull request Apr 19, 2019
* service.Service type is now logicless and its relationship with Docker Containers will be managed by any implementation of manager.Manager.
* Manager is responsible for managing Docker Containers of MESG Services. it can be implemented for any container orchestration tool. right now, Docker Service is the only one we have.

related to #799.
ilgooz added a commit that referenced this pull request Apr 19, 2019
service.Service type is now logicless and its relationship with Docker Containers will be managed by any implementation of manager.Manager.

Manager is responsible for managing Docker Containers of MESG Services. it can be implemented for any container orchestration tool. right now, Docker Service is the only one we have.

* service: Service.GetDependency(), Service.Namespace() & Dependency.Namespace() made public.

related to #799.
ilgooz added a commit that referenced this pull request Apr 19, 2019
service.Service type is now logicless and its relationship with Docker Containers will be managed by any implementation of manager.Manager.

Manager is responsible for managing Docker Containers of MESG Services. it can be implemented for any container orchestration tool. right now, Docker Service is the only one we have.

* service: Service.GetDependency(), Service.Namespace() & Dependency.Namespace() are made public.

related to #799.
ilgooz added a commit that referenced this pull request Apr 19, 2019
service.Service type is now logicless and its relationship with Docker Containers will be managed by any implementation of manager.Manager.

Manager is responsible for managing Docker Containers of MESG Services. it can be implemented for any container orchestration tool. right now, Docker Service is the only one we have.

* service: Service.GetDependency(), Service.Namespace() & Dependency.Namespace() func are made public.

related to #799.
ilgooz added a commit that referenced this pull request Apr 19, 2019
service.Service type is now logicless and its relationship with Docker Containers will be managed by any implementation of manager.Manager.

Manager is responsible for managing Docker Containers of MESG Services. it can be implemented for any container orchestration tool. right now, Docker Service is the only one we have.

* service: Service.GetDependency(), Service.Namespace() & Dependency.Namespace() func are made public.

related to #799.
ilgooz added a commit that referenced this pull request Apr 19, 2019
service.Service type is now logicless and its relationship with Docker Containers will be managed by any implementation of manager.Manager.

Manager is responsible for managing Docker Containers of MESG Services. it can be implemented for any container orchestration tool. right now, Docker Service is the only one we have.

* service: Service.GetDependency(), Service.Namespace() & Dependency.Namespace() func are made public.

related to #799.
ilgooz added a commit that referenced this pull request Apr 19, 2019
service.Service type is now logicless and its relationship with Docker Containers will be managed by any implementation of manager.Manager.

Manager is responsible for managing Docker Containers of MESG Services. it can be implemented for any container orchestration tool. right now, Docker Service is the only one we have.

* new manager pkg put under service/manager.
* new dockermanager pkg put under service/manager/dockermanager. its logic is consist of the code split from service pkg.
* service: Service.GetDependency(), Service.Namespace() & Dependency.Namespace() func are made public.
* rename Manager.DeleteVolumes() as Manager.Delete().

related to #799.
ilgooz added a commit that referenced this pull request Apr 19, 2019
service.Service type is now logicless and its relationship with Docker Containers will be managed by any implementation of manager.Manager.

Manager is responsible for managing Docker Containers of MESG Services. it can be implemented for any container orchestration tool. right now, Docker Service is the only one we have.

* new manager pkg put under service/manager.
* new dockermanager pkg put under service/manager/dockermanager. its logic is consist of the code split from service pkg.
* service: Service.GetDependency(), Service.Namespace() & Dependency.Namespace() funcs are made public.
* service/manager: rename/have Manager.DeleteVolumes() as Manager.Delete().

related to #799.
@NicolasMahe
Copy link
Copy Markdown
Member

Closing this PR as it's not relevant anymore

@NicolasMahe NicolasMahe closed this Jun 3, 2019
@NicolasMahe NicolasMahe deleted the service-refactor branch June 3, 2019 11:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants