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

Added minimal cluster support #331

Merged
merged 15 commits into from
Dec 12, 2018
Merged

Conversation

felix-engelmann
Copy link
Contributor

Added support to query cluster members and specify a target for creation of containers in a cluster

Signed-off-by: Felix Engelmann <fe-github@nlogn.org>
Signed-off-by: Felix Engelmann <fe-github@nlogn.org>
…eate

Signed-off-by: Felix Engelmann <fe-github@nlogn.org>
Signed-off-by: Felix Engelmann <fe-github@nlogn.org>
Signed-off-by: Felix Engelmann <fe-github@nlogn.org>
Signed-off-by: Felix Engelmann <fe-github@nlogn.org>
@codecov-io
Copy link

codecov-io commented Oct 8, 2018

Codecov Report

Merging #331 into master will decrease coverage by 0.04%.
The diff coverage is 96.15%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master    #331      +/-   ##
=========================================
- Coverage   96.84%   96.8%   -0.05%     
=========================================
  Files          11      12       +1     
  Lines         919     970      +51     
  Branches      106     108       +2     
=========================================
+ Hits          890     939      +49     
- Misses         10      12       +2     
  Partials       19      19
Impacted Files Coverage Δ
pylxd/client.py 98.69% <100%> (+0.05%) ⬆️
pylxd/managers.py 100% <100%> (ø) ⬆️
pylxd/models/container.py 91.1% <100%> (ø) ⬆️
pylxd/models/cluster.py 94.59% <94.59%> (ø)

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 8e57eb0...a22c401. Read the comment docs.

@felix-engelmann
Copy link
Contributor Author

I have this line in the model ClusterMember

    @property
    def api(self):
        return self.client.api.cluster_members[self.name]

which is not covered by tests, I would have removed it, but it feels like it is important somewhere

Signed-off-by: Felix Engelmann <fe-github@nlogn.org>
Copy link
Contributor

@ajkavanagh ajkavanagh left a comment

Choose a reason for hiding this comment

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

Firstly, thanks for submitting the PR. It's great to get adding clustering support moving along. I have a minor change to the code, along with a more substantial request.

The main premise for the change appears to be to add just support for the 'cluster/members" endpoint, and ignore the "cluster" endpoint. I'm fine with that, in general, but I wonder whether there should be an intermediary Cluster manager?

pylxd/client.py Outdated
@@ -71,6 +71,8 @@ def __getattr__(self, name):
# Special case for storage_pools which needs to become 'storage-pools'
if name == 'storage_pools':
name = 'storage-pools'
if name == 'cluster_members':
name = 'cluster/members'
Copy link
Contributor

Choose a reason for hiding this comment

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

This confused me when I first looked at it, and then I realised that the ClusterMemberManager is straddling the cluster segment of the endpoint.

This means there is no ClusterManager for cluster? I feel there should be a ClusterManager and then a ClusterMemberManager that hangs off that for members.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for your comments. I tried a lot to have a ClusterManager and then a hierarchical ClusterMemberManager , but as there are no multiple cluster elements and /cluster/members is a more or less independent endpoint from /cluster I argue against such a hierarchy. It would take quite some tweaking to get the ClusterMemberManager below the ClusterManager, because unlike with the nested StoragePoolManagers, there is no such thing as a /cluster/0/members endpoint.
What is your opinion on it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So a ClusterMemberManager and a ClusterManager can coexist as top level managers. There is also little semantic relation.

Copy link
Contributor

Choose a reason for hiding this comment

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

It feels wrong. My rationale is to help people who are reading the REST API documents; i.e. it's the difference between

client.cluster.members.all()

# and

client.cluster_members.all()

At the moment we have a "rule" that is effectively 'replace a / with a . in python code and add all() , get() etc to the end.

TBH, I'm not a big fan of the current API format (I didn't design it, so take my criticism with a grain of salt, as it might be NIH!!), but I'm holding out to keep it consistent.

However, if it's a lot of work to make the change, then let me know here. I'm just thinking of an empty manager that can then have another manager hung off of it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree that client.cluster.members.all() feels better. However I did not manage to write such a nested Manager, where there exists only one object.

Here is how I tried to build it with a ClusterManager:

from pylxd.models import _model as model
from pylxd import managers

class Cluster(model.Model):
    """A LXD certificate."""

    server_name = model.Attribute(readonly=True)
    enabled = model.Attribute(readonly=True)

    members = model.Manager()

    def __init__(self, *args, **kwargs):
        super(Cluster, self).__init__(*args, **kwargs)

        self.members = ClusterMemberManager(self)


    @classmethod
    def get(cls, client):
        """Get a certificate by fingerprint."""

        client.assert_has_api_extension('clustering')
        response = client.api.cluster.get()

        return cls(client, **response.json()['metadata'])

    @property
    def api(self):
        print("in api:", type( self.client.api.cluster), " other type",  type(self.client.api.cluster[0]))
        print("in api:", self.client.api.cluster._api_endpoint, " other type",  self.client.api.cluster[0]._api_endpoint)
        return self.client.api.cluster

and it's child the ClusterMemberManager:

class ClusterMemberManager(managers.BaseManager):
    manager_for = 'pylxd.models.ClusterMember'


class ClusterMember(model.Model):
    name = model.Attribute(readonly=True)
    url = model.Attribute(readonly=True)
    database = model.Attribute(readonly=True)
    state = model.Attribute(readonly=True)
    server_name = model.Attribute(readonly=True)
    status = model.Attribute(readonly=True)
    message = model.Attribute(readonly=True)

    cluster = model.Parent()

    @property
    def api(self):
        print("in member api:",type(self))
        print("in member api:",dir(self))
        print("in member api:",self.__dir__())
        print("in member api:",self.__attributes__)
        print("in member api:",self.cluster._api_endpoint)
        print("in member api[0]:",type(self.cluster[0]))
        print("in member api[0]:",self.cluster[0]._api_endpoint)
        return self.cluster.api.members[self.name]

    @classmethod
    def get(cls, cluster, name):
        """Get a certificate by fingerprint."""
        print("here")
        response = cluster.api.members[name].get()

        #return cls(cluster.client, **response.json()['metadata'])
        return cls(cluster.client, **response.json()['metadata'])

    @classmethod
    def all(cls, cluster):
        response = cluster.api.members.get()

        nodes = []
        for node in response.json()['metadata']:
            name = node.split('/')[-1]
            nodes.append(cls(cluster.client, name=name))
        return nodes

As you see there is quite a lot of debug outputs inside. I screwed up the api references somehow, because the Cluster.api should return sth like self.client.api.cluster[0] but that would query /1.0/cluster/0 which is wrong.
Do you have a quick idea on how to fix this?

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, interesting; it's sort of been done before with the snapshots manager, but those were on a container (obviously a list of containers). But I'm not sure how to do it ... I'll have a think and get back to you. I'll grab your master and see what I can see.

Copy link
Contributor Author

@felix-engelmann felix-engelmann Dec 11, 2018

Choose a reason for hiding this comment

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

this is not pushed, just an idea. You find my WIP at: https://github.com/felix-engelmann/pylxd/tree/nested-cluster-manager

Copy link
Contributor

Choose a reason for hiding this comment

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

I kind of know what we're trying to do; "basically, create an object to hang .members off that does-the-right-thing; I'll have a ponder at the code.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hi @felix-engelmann -- so I've had a go; not sure if I like it! What are your thoughts? It's here: https://github.com/ajkavanagh/pylxd/tree/felix-ajk-alternative, commit: ajkavanagh@755185a

I ended up hacking up a custom ClusterManager to be able to have the singleton and also access members; it looks like the code architecture wasn't designed to do it!

Also, it needs a bit of work (e.g. tests, etc.) -- is it heading in a direction that you'd be happy with?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thx, cool! I was forcing it too much into the existing structure, what failed. But your solution looks as it is going in the right direction. I'll pick it up from there and adapt the tests etc.

pylxd/client.py Outdated Show resolved Hide resolved
pylxd/client.py Outdated Show resolved Hide resolved
Signed-off-by: Felix Engelmann <fe-github@nlogn.org>
Adapted it that the class directly exposes .members
@Property of members with getter to _members did not work

missing tests

Signed-off-by: Felix Engelmann <fe-github@nlogn.org>
Signed-off-by: Felix Engelmann <fe-github@nlogn.org>
Signed-off-by: Felix Engelmann <fe-github@nlogn.org>
@felix-engelmann
Copy link
Contributor Author

What's your opinion on this? There are still a few print debugs. Was there any particular reason to hide the _members behind a @property getter?

pylxd/models/cluster.py Outdated Show resolved Hide resolved
@ajkavanagh
Copy link
Contributor

What's your opinion on this? There are still a few print debugs. Was there any particular reason to hide the _members behind a @property getter?

TBH I was playing around with how to get it to work, and it "became" a property as I was trying things out. No, I guess there is no need for it to be a @property getter.

Otherwise, yes, I think it is good. If you can get some tests around it we'll land it. Nice addition! 👍

Signed-off-by: Felix Engelmann <fe-github@nlogn.org>
Signed-off-by: Felix Engelmann <fe-github@nlogn.org>
@ajkavanagh
Copy link
Contributor

Are you happy with this now? Tests are passing and if it does the minimum you were looking for, then it's good to merge now.

Signed-off-by: Felix Engelmann <fe-github@nlogn.org>
@felix-engelmann
Copy link
Contributor Author

felix-engelmann commented Dec 12, 2018

I added a test for the Cluster itself, so I'm happy with it now. Thanks for your cooperation. If you want to release, please bump the version, I did not.

@ajkavanagh
Copy link
Contributor

Okay, thanks very much for the PR -- let's get it merged.

@ajkavanagh ajkavanagh merged commit dcff036 into canonical:master Dec 12, 2018
ajkavanagh pushed a commit that referenced this pull request Dec 13, 2018
Add initial cluster documentation for PR #331

Signed-off-by: Felix Engelmann <fe-github@nlogn.org>
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.

3 participants