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

Secrets manager and base provider proposal #2758

Merged
merged 9 commits into from
Feb 13, 2017

Conversation

protatremy
Copy link
Contributor

@protatremy protatremy commented Feb 8, 2017

Proposal to implement Buildbot secrets service:

  • secrets manager (get API)
  • secret provider base

Contributor Checklist:

  • I have updated the unit tests
  • I have created a file in the master/buildbot/newsfragment directory (and read the README.txt in that directory)
  • I have updated the appropriate documentation

Copy link
Member

@tardyp tardyp left a comment

Choose a reason for hiding this comment

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

Looks pretty solid! congrats!
a few nitpicks to fix goal is to go 100% coverage

"""
source of the secret
"""
return self._source
Copy link
Member

Choose a reason for hiding this comment

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

please try to get 100% coverage. install codecov.io chrome extension to see that this line is not covered

- source: provider to retrieve secret
- key: secret key identifier
- value: secret value
- props: all other needed properties
Copy link
Member

Choose a reason for hiding this comment

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

what kind of properties do we need for secrets?

class SecretDetails(object):
"""
A SecretDetails object has secrets attributes:
- source: provider to retrieve secret
Copy link
Member

Choose a reason for hiding this comment

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

provider where the secret was retrieved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

replaced

@codecov
Copy link

codecov bot commented Feb 8, 2017

Codecov Report

Merging #2758 into master will increase coverage by 0.02%.
The diff coverage is 100%.

@@            Coverage Diff             @@
##           master    #2758      +/-   ##
==========================================
+ Coverage   87.31%   87.33%   +0.02%     
==========================================
  Files         305      308       +3     
  Lines       32564    32622      +58     
==========================================
+ Hits        28432    28491      +59     
+ Misses       4132     4131       -1
Impacted Files Coverage Δ
master/buildbot/secrets/secret.py 100% <100%> (ø)
master/buildbot/secrets/providers/base.py 100% <100%> (ø)
master/buildbot/secrets/manager.py 100% <100%> (ø)
worker/buildbot_worker/init.py 87.09% <ø> (-0.41%)
master/buildbot/init.py 89.65% <ø> (-0.35%)
master/buildbot/scripts/cleanupdb.py 96.49% <ø> (-0.07%)
master/buildbot/db/pool.py 86.92% <ø> (ø)
... and 5 more

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 16a3c75...abe4e0a. Read the comment docs.

secret_detail = None
providers = self.master.config.secretsManagers
for provider in providers:
value = provider.get(secret)
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 we need this get to be yield. Some providers will need to make network access to do the get

"""
name = 'secrets'

def get(self, secret, *args, **kwargs):
Copy link
Member

Choose a reason for hiding this comment

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

this function shall return deferred (inlinecallbacks)

def get(self, *args, **kwargs):
"""
this should be an abstract method
"""
Copy link
Member

Choose a reason for hiding this comment

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

the you can mark it abstract https://docs.python.org/2/library/abc.html

def __init__(self, provider, key, value, props=None):

A ``secretDetails`` is a python object initialized with the following parameters:
- provider name to retrieve secrets,
Copy link
Member

Choose a reason for hiding this comment

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

name of provider where secret has been retrieved

- value returned by the provider API
- properties if needed.

Each parameter is an object property that should returned the value.
Copy link
Member

Choose a reason for hiding this comment

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

its not very clear to me what you mean


c['secretsProviders'] = [SecretsProviderOne(params), SecretsProviderTwo(params)]

If more than one provider is defined in the configuration, the manager returns the first founded value.
Copy link
Member

Choose a reason for hiding this comment

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

the manager returns the first value found.

secretDetails = secretsService.get(secret)

The get API take the secret key as parameters and read the configuration to obtains the list of configured providers.
The manager get the selected provider and returns a ``SecretDetails``.
Copy link
Member

Choose a reason for hiding this comment

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

+The manager call the get method of the configured provider and returns a SecretDetails if the call succeed.

"other2": "value"})
]
SecretManager.master = self.master
expectedSecretDetail = SecretDetails(
Copy link
Member

Choose a reason for hiding this comment

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

If not found, I think it should return None, not secretDefails(fakestorage2, None)

Copy link
Contributor Author

@protatremy protatremy Feb 10, 2017

Choose a reason for hiding this comment

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

I preferred to return the object because it contains the provider class name information, that will be used to prompt a log error indication from where the information has not been found.

we spoke about that together, I changed it. The get API returns the first found value else returns None

]
SecretManager.master = self.master
expectedSecretDetail = SecretDetails(
OtherFakeSecretStorage({"foo2": "bar",
Copy link
Member

Choose a reason for hiding this comment

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

why dont you just write OtherFakeSecretStorage.__name__ ?

get is an abstract method in the provider base
fixes in the rst
"""
secret_detail = None
providers = self.master.config.secretsManagers
for provider in providers:
Copy link
Member

Choose a reason for hiding this comment

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

you should return the first that is not None.

for provider in providers:
    value = yield provider.get(secret)
    if value is not None:
        source_name = provider.__class__.__name__
        defer.returnValue(SecretDetails(source_name, secret, value))
# return None is implicit

"foo3",
None)
secret_result = yield secret_service_manager.get("foo3")
self.assertEqual(secret_result, expectedSecretDetail)
Copy link
Member

Choose a reason for hiding this comment

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

Need to add a test for 2 providers having the secret in their DB (but not the same value):
should return the value of the first provider

# this program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Copyright Buildbot Team Members
Copy link
Member

Choose a reason for hiding this comment

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

We will have several providers, so we should call this module buildbot.secrets.providers (with an s)

for provider in providers:
value = yield provider.get(secret)
source_name = provider.__class__.__name__
if value:
Copy link
Member

Choose a reason for hiding this comment

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

if value is not None (a secret might be empty string)

]
SecretManager.master = self.master
secret_result = yield secret_service_manager.get("foo3")
self.assertEqual(secret_result, None)
Copy link
Member

Choose a reason for hiding this comment

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

I don't see the test with two providers providing the same secret.

Need another test with a secret value being empty string ( empty string is valid secret)

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.

None yet

2 participants