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

Add in-cluster config support #32

Merged
merged 1 commit into from
Nov 23, 2016
Merged

Add in-cluster config support #32

merged 1 commit into from
Nov 23, 2016

Conversation

mbohlool
Copy link
Contributor

enables client-python to be used inside a kubernetes cluster.

Fixes #26

@mbohlool mbohlool added this to the v1.0 milestone Nov 22, 2016
@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Nov 22, 2016
@mbohlool mbohlool changed the title Add incluster config support Add in-cluster config support Nov 22, 2016
@marun
Copy link

marun commented Nov 22, 2016

What's the plan for enabling automated testing (unit/integration/e2e)? There's only so much a review of code can do, and I'd rather be reviewing code + tests, and have those tests run automatically.

@mbohlool
Copy link
Contributor Author

mbohlool commented Nov 22, 2016

@marun Thanks for demanding test from a lazy engineer here :) It is in much better shape now.
Refactored code to be able to test and added tests. Please take another look.

@mbohlool
Copy link
Contributor Author

@marun would you please take another look at this? I want to create a release after this PR.

Copy link

@marun marun left a comment

Choose a reason for hiding this comment

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

I'm happy to see tests. Is there a timeline on automatic execution ci and e2e (so that client behavior can be validated against a real cluster)?

def _join_host_port(host, port):
"""Adapted golang's net.JoinHostPort"""
if ':' in host or '%' in host:
return "[%s]:%s" % (host, str(port))
Copy link

Choose a reason for hiding this comment

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

(No action required)

Consider avoiding explicitly calling str() (i.e. '%s' % 42 works just fine).

Also, consider the following alternate implementation:

template = '%s:%s'
# Where possible, assign a conditional statement to a variable and name it descriptively.
# This can reduce the need for comments, which cost more to maintain than code that is self-documenting.
host_requires_bracketing = ':' in host or '%' in host
if host_requires_bracketing:
  template = '[%s]:%s'
return template % (host, port)

if not os.path.isfile(self._token_filename):
raise ConfigException("Service token file does not exists.")

with open(self._token_filename, 'r') as f:
Copy link

Choose a reason for hiding this comment

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

(No action required)

'r' is the default and can be omitted.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

self._environ[self._port_env_name]))

if not os.path.isfile(self._token_filename):
raise ConfigException("Service token file does not exists.")
Copy link

Choose a reason for hiding this comment

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

Consider separating reading of cluster config from modifying configuration to ensure it is only modified once configuration has been validated. This separation of concerns will also allow simplify testing by allowing the read values to be checked without involving configuration:

# Consider using namedtuple instead of a dict for simple types
ClusterConfig = collections.NamedTuple('ClusterConfig', ['host', 'token'...])

def load_incluster_config(environ=os.environ):
  conf = read_incluster_config(environ=environ)
  set_incluster_config(conf)

def read_incluster_config(environ=os.environ):
  # Consider checking the value of env vars rather than the presence of the name to protect against empty vars
  service_host = environ.get(_SERVICE_HOST_ENV_NAME)
  if not service_host:
    raise ConfigException("Service host not set.")
  ...
  host = 'https://%s' % (_join_host_port(service_host, service_port)
  return ClusterConfig(host, token...)

def set_incluster_config(conf):
  configuration.host = conf.host
  ...

This will also avoid the problem of modifying configuration in one unit test and accidentally having that modification affect the behavior of a different test that relies on its state. Due to configuration being a singleton, any changes to its default state must be reverted on test teardown.

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've used the InClusterConfigLoader to store the values and separate load and set methods.


from .incluster_config import ConfigException, InClusterConfigLoader

_TEST_PORT_ENV_NAME = "port"
Copy link

Choose a reason for hiding this comment

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

Consider reusing the names of the env vars defined as constants in the module under test (e.g. _SERVICE_HOST_ENV_NAME) rather than defining new names in this test module. Since the environ dict is passed to the function under test, there is no need to vary the names of the env vars and the names can be fixed across runtime and test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@mbohlool
Copy link
Contributor Author

@marun I am going to discuss ci and e2e with kubernetes e2e test folks in a separate thread/issue.

@mbohlool
Copy link
Contributor Author

@marun, I've addressed all of your comments. As all of them were optional, I assume this PR is look good. Please apply LGTM next time if your comments are all optional. Thanks for the review.

@mbohlool mbohlool added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Nov 23, 2016
raise ConfigException("Service token file does not exists.")

with open(self._token_filename) as f:
self.token = f.read()
Copy link

Choose a reason for hiding this comment

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

What if the file is empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Missed these comments, addressed them in #50

self._set_config()

def _load_config(self):
if (self._host_env_name not in self._environ or
Copy link

Choose a reason for hiding this comment

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

What about the case where either of the env vars are defined but empty (e.g. KUBERNETES_SERVICE_HOST=)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Missed these comments, addressed them in #50

@mbohlool mbohlool merged commit 9506c55 into master Nov 23, 2016
@mbohlool mbohlool deleted the o4 branch December 8, 2016 06:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants