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

Kadet input type *experimental* #190

Merged
merged 11 commits into from Jan 15, 2019

Conversation

Projects
None yet
3 participants
@ramaro
Copy link
Collaborator

ramaro commented Jan 9, 2019

This introduces a new experimental input type called Kadet.

Kadet is essentially a Python module offering a set of classes and functions to define objects which will compile to JSON or YAML. A complete example is available in examples/kubernetes/components/nginx.

Overview

BaseObj

BaseObj implements the basic object implementation that compiles into JSON or YAML.
Setting keys in self.root means they will be in the compiled output. Keys can be set as an hierarchy of attributes (courtesy of addict)
The self.body() method is reserved for setting self.root on instantiation:

The example below:

class MyApp(BaseObj):
 def body(self):
   self.root.name = "myapp"
   self.root.inner.foo = "bar"
   self.root.list = [1, 2, 3]

compiles into:

---
name: myapp
inner:
  foo: bar
list:
  - 1
  - 2
  - 3

The self.new() method can be used to define a basic constructor.
self.need() checks if a key is set and errors if it isn't (with an optional custom error message).
kwargs that are passed onto a new instance of BaseObj are always accessible via self.kwargs
In this example, MyApp needs name and foo to be passed as kwargs.

class MyApp(BaseObj):
 def new(self):
   self.need("name")
   self.need("foo", msg="please provide a value for foo")

 def body(self):
   self.root.name = self.kwargs.name
   self.root.inner.foo = self.kwargs.foo
   self.root.list = [1, 2, 3]

obj = MyApp(name="myapp", foo="bar")

Setting a skeleton

Defining a large body with Python can be quite hard and repetitive to read and write.
The self.update_root() method allows importing a YAML/JSON file to set the skeleton of self.root.

MyApp's skeleton can be set instead like this:

#skel.yml
---
name: myapp
inner:
  foo: bar
list:
  - 1
  - 2
  - 3
class MyApp(BaseObj):
 def new(self):
   self.need("name")
   self.need("foo", msg="please provide a value for foo")
   self.update_root("path/to/skel.yml")

Extending a skeleton'd MyApp is possible just by implementing self.body():

class MyApp(BaseObj):
 def new(self):
   self.need("name")
   self.need("foo", msg="please provide a value for foo")
   self.update_root("path/to/skel.yml")

 def body(self):
   self.set_replicas()
   self.root.metadata.labels = {"app": "mylabel"}

def set_replicas(self):
   self.root.spec.replicas = 5

Inheritance

Python inheritance will work as expected:

class MyOtherApp(MyApp):
  def new(self):
    super().new()  # MyApp's new()
    self.need("size")

def body(self):
   super().body()  #  we want to extend MyApp's body
   self.root.size = self.kwargs.size
   del self.root.list  # get rid of "list"

obj = MyOtherApp(name="otherapp1", foo="bar2", size=3)

compiles to:

---
name: otherapp1
inner:
  foo: bar2
replicas: 5
size: 3

Components

A component in Kadet is a python module that must implement a main() function returning an instance of BaseObj. The inventory is also available via the inventory() function.

For example, a tinyapp component:

# components/tinyapp/__init__.py
from kapitan.inputs.kadet import BaseOBj, inventory
inv = inventory() # returns inventory for target being compiled

class TinyApp(BaseObj):
  def body(self):
    self.root.foo = "bar"
    self.root.replicas = inv.parameters.tinyapp.replicas

def main():
  obj = BaseOb()
  obj.root.deployment = TinyApp() # will compile into deployment.yml
  return obj

An inventory class must be created for tinyapp:

# inventory/classes/components/tinyapp.yml

parameters:
  tinyapp:
    replicas: 1
  kapitan:
    compile:
    - output_path: manifests
      input_type: kadet
      output_type: yaml
      input_paths:
        - components/tinyapp

Common components

A library in --search-paths (which now defaults to . and lib/) can also be a module that kadet components import. It is loaded using the load_from_search_paths():

kubelib = load_from_search_paths("kubelib") # lib/kubelib/__init__.py

def main():
  obj = BaseObj()
  obj.root.example_app_deployment = kubelib.Deployment(name="example-app")
  return obj

@ramaro ramaro force-pushed the ramaro:kadet_input_type branch from cdf1a5f to 56822f1 Jan 9, 2019

Show resolved Hide resolved kapitan/inputs/kadet.py Outdated
Show resolved Hide resolved kapitan/inputs/kadet.py Outdated
Show resolved Hide resolved tests/test_kadet.py Outdated
Show resolved Hide resolved tests/test_kadet.py Outdated
Show resolved Hide resolved tests/test_kadet.py Outdated
@uberspot

This comment has been minimized.

Copy link
Collaborator

uberspot commented Jan 10, 2019

I'd add the text you added in the PR to a separate Kadet.md and link to it from the main README.md. Or maybe move that and the entire Modes of operation section to a separate file(?). That could be a different PR, but it would make the Readme easier to digest.

ramaro added some commits Jan 13, 2019

@ramaro ramaro merged commit 7c4daec into deepmind:master Jan 15, 2019

2 checks passed

cla/google All necessary CLAs are signed
continuous-integration/travis-ci/pr The Travis CI build passed
Details

@ramaro ramaro referenced this pull request Apr 1, 2019

Open

Document the Kadet input #255

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.