Developer guide

Contributing

There are large parts of the Kubernetes API that are not covered by this library yet, but we welcome anyone who wishes to help us make it more complete.

If you want to contribute, read the rest of this guide, open an issue to discuss the changes you want to do (if you feel it is needed), and finally send us a Pull Request with your changes.

We strive to have good test coverage, and PRs with failing or ignored tests will not be accepted. We also encourage you to add at least a minimal set of tests for the new code you write. Since some parts of the library are harder to test than others, look at other, similar, parts of the library to see the level of testing wanted.

We use Prospector for code quality/style checking, and PRs failing this check might be required to fix any issues before being merged. The code style in the project is close to PEP8, so should not present any big problems.

Adding support for new object types

If you want to create support for a new type of object in the Kubernetes API, the best thing to start with is the Kubernetes API reference documentation. The first thing you need to do, is find the “top most” type, ie. the type you operate on through the API. We can call this an API type. Examples are Pod, Service or Ingress.

Create a new module under k8s.models if your type doesn’t belong in any of the existing ones, then start by creating a class inheriting from Model named the same as the type is named in the Kubernetes documentation. Use the same casing as the Kubernetes documentation uses.

For your API type, inside the class you should declare an inner class called Meta, which has a single field url_template. This should be the URL template used when getting, creating or updating objects of this type (see the above mentioned examples).

For each field in the Kubernetes documentation, add a field to the class named exactly the same (including case). If the name is an invalid python identifier, add a _ suffix (so exec becomes exec_). The value of the field should be an instance of a subclass of Field, depending on the semantics of the field.

Use this class…

…when

ListField

the field is a list (aka array)

OnceField

the field can only be set on new objects

RequiredField

the field is required

ReadOnlyField

the field is set by the API server

Field

none of the above applies

The Field class takes three parameters:

type

The type of value this field contains. Can be simple types (int, bool etc), datetime.datetime or subclasses of Model.

default_value

The field is set to this value when an instance of the class is created. The default default is None.

alt_type

The Kubernetes API will in some cases accept two types for a field (usually integer and string). This is the less common of the two, otherwise it has the same meaning as type.

This parameter is not available for the ListField, but so far we have not come across any case where it is needed.

Once you have created a class for your API type, some of the fields will refer to new types which have yet to be defined. Make sure to use existing types defined elsewhere (possibly moving them to k8s.models.common if they are used in multiple places. Continue defining new subclasses of Model for each type needed, until you have created all the types required for your API type to be completely specified.

Note

If the Kubernetes documentation says the type is object, the python type should be dict. If the Kubernetes documentation says the type is string, the python type should be str. Most other simple types are obvious.

How the HorizontalPodAutoscaler type is implemented
 1# Unless required by applicable law or agreed to in writing, software
 2# distributed under the License is distributed on an "AS IS" BASIS,
 3# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 4# See the License for the specific language governing permissions and
 5# limitations under the License.
 6
 7
 8from .common import ObjectMeta
 9from ..base import Model
10from ..fields import Field, RequiredField
11
12
13class CrossVersionObjectReference(Model):
14    kind = RequiredField(str)
15    name = RequiredField(str)
16    apiVersion = Field(str)
17
18
19class HorizontalPodAutoscalerSpec(Model):
20    scaleTargetRef = RequiredField(CrossVersionObjectReference)
21    minReplicas = Field(int, 2)
22    maxReplicas = RequiredField(int)
23    targetCPUUtilizationPercentage = Field(int, 50)
24
25
26class HorizontalPodAutoscaler(Model):
27    class Meta:
28        url_template = "/apis/autoscaling/v1/namespaces/{namespace}/horizontalpodautoscalers/{name}"
29
30    metadata = Field(ObjectMeta)
31    spec = Field(HorizontalPodAutoscalerSpec)

Releasing a new version

To make a new release there are a couple steps to follow. Ideally, we want to release from master, as often as possible. Version numbers should adhere to SemVer. When you have a passing build that you want to make a release from, do the following steps:

  • Create an annotated tag for the commit in question, naming it v<major>.<minor>.<bugfix>. For instance:

    $ git tag -a v0.0.2 a1b2c3d4
    
  • Push the new tag to github:

    $ git push origin v0.0.2
    
  • A new release with the version you selected as a tag should now be built and uploaded to PyPI and Github