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

[docs] Convention Server Open API spec #74

Merged
merged 13 commits into from
Apr 26, 2022
Merged
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
go-version: 1.18.x
- name: Test
run: make test
- uses: codecov/codecov-action@v2
- uses: codecov/codecov-action@v3
katmutua marked this conversation as resolved.
Show resolved Hide resolved
- name: Build
run: |
set -o errexit
Expand Down
90 changes: 90 additions & 0 deletions api/openapi-spec/conventions-server..yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
openapi: 3.0.0
katmutua marked this conversation as resolved.
Show resolved Hide resolved
info:
title: Conventions server
katmutua marked this conversation as resolved.
Show resolved Hide resolved
description: >-
"a sample conventions server"
version: 1.0.0
katmutua marked this conversation as resolved.
Show resolved Hide resolved
license:
name: Apache-2.0
url: 'https://www.apache.org/licenses/LICENSE-2.0.html'
paths:
/webhook:
post:
description: "a sample conventions server"
katmutua marked this conversation as resolved.
Show resolved Hide resolved
responses:
katmutua marked this conversation as resolved.
Show resolved Hide resolved
200:
description: Successfully applied defined conventions
content:
"application/json":
schema:
$ref: '#/components/schemas/PodConventionContext'
400:
description: Error decoding PodConventionContext from request body or request body is nil
500:
description: Internal server error
content:
"application/json":
schema:
$ref: '#/components/schemas/ErrorResponse'
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think the client distinguishes between different types of http errors. Either the webhook call succeeds or it doesn't. Better alignment with Kubernetes by following the model for AdmissionReview response would be good, but is a separate issue.

components:
schemas:
PodConventionContext:
type: object
properties:
katmutua marked this conversation as resolved.
Show resolved Hide resolved
spec:
$ref: "#/components/schemas/PodConventionContextSpec"
status:
$ref: "#/components/schemas/PodConventionContextStatus"
PodConventionContextSpec:
type: object
properties:
Template:
katmutua marked this conversation as resolved.
Show resolved Hide resolved
$ref: "#/components/schemas/PodTemplateSpec"
ImageConfig:
katmutua marked this conversation as resolved.
Show resolved Hide resolved
$ref: "#/components/schemas/ImageConfig"
PodConventionContextStatus:
type: object
properties:
Template:
katmutua marked this conversation as resolved.
Show resolved Hide resolved
$ref: "#/components/schemas/PodTemplateSpec"
appliedConventions:
description: a string array of conventions to be applied
type: object
katmutua marked this conversation as resolved.
Show resolved Hide resolved
ImageConfig:
type: object
properties:
image:
type: string
description: a string reference to the image name and tag or associated digest
boms:
$ref: "#/components/schemas/BOM"
katmutua marked this conversation as resolved.
Show resolved Hide resolved
config:
type: object
description: "a ggcrv1 config file object"
Copy link
Contributor

Choose a reason for hiding this comment

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

The config object is quite large. Most of it is structured, and parts are unstructured.

I've been wondering if we should make the config in the webhook contract schema-less (what you have here). We promise to expose the metadata we can find, it's up to the registry to define that metadata in a structured way for the image. In that sense, we shouldn't define the config as a ggcr type, even if we use ggcr to lookup that metadata.

If we go this route, clients that do care about the OCI metadata will need to convert the raw blob into an appropriate type. Which isn't too different that how clients need to parse the SBOM

What do you think?

BOM:
type: object
properties:
name:
type: string
raw:
description: an array of bytes
type: array
items:
type: object
katmutua marked this conversation as resolved.
Show resolved Hide resolved
PodTemplateSpec:
type: object
properties:
spec:
$ref: "kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#pod-v1-core"
Copy link
Contributor

Choose a reason for hiding this comment

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

does this type of ref work? I know JSON Ref can use URLs, this doesn't look like a URL and Kubernetes.io would need to publish the schema for each type in open api.

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
$ref: "kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#pod-v1-core"
$ref: "kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#pod-v1-core"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The intention was to reference the podTemplateSpec definition from a valid kubernetes openapi spec similar to

components:
  schemas:
    User:
      $ref: 'https://api.example.com/v2/openapi.yaml#/components/schemas/User'

description: specification of the desired behavior of a pod
ErrorResponse:
type: object
properties:
error:
$ref: "#/components/schemas/Error"
Error:
type: object
properties:
message:
type: string
example: "Some unexpected error"
2 changes: 1 addition & 1 deletion config/default/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namePrefix: cartographer-conventions-

# Labels to add to all resources and selectors.
commonLabels:
component: conventions.carto.run
app.kubernetes.io/component: conventions

bases:
- ../crd
Expand Down
2 changes: 1 addition & 1 deletion dist/bundle.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: e30K

#@overlay/match by=overlay.subset({"apiVersion":"apps/v1","kind":"Deployment","metadata":{"labels":{"component":"conventions.carto.run"}}})
#@overlay/match by=overlay.subset({"apiVersion":"apps/v1","kind":"Deployment","metadata":{"labels":{"app.kubernetes.io/component":"conventions"}}})
---
spec:
template:
Expand Down
40 changes: 20 additions & 20 deletions dist/cartographer-conventions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.8.0
labels:
component: conventions.carto.run
app.kubernetes.io/component: conventions
name: clusterpodconventions.conventions.carto.run
spec:
group: conventions.carto.run
Expand Down Expand Up @@ -112,7 +112,7 @@ metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.8.0
labels:
component: conventions.carto.run
app.kubernetes.io/component: conventions
duck.knative.dev/podspecable: "true"
name: podintents.conventions.carto.run
spec:
Expand Down Expand Up @@ -6328,7 +6328,7 @@ apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
labels:
component: conventions.carto.run
app.kubernetes.io/component: conventions
name: cartographer-conventions-leader-election-role
namespace: cartographer-system
rules:
Expand Down Expand Up @@ -6390,7 +6390,7 @@ kind: ClusterRole
metadata:
creationTimestamp: null
labels:
component: conventions.carto.run
app.kubernetes.io/component: conventions
name: cartographer-conventions-manager-role
rules:
- apiGroups:
Expand Down Expand Up @@ -6474,7 +6474,7 @@ apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
component: conventions.carto.run
app.kubernetes.io/component: conventions
name: cartographer-conventions-proxy-role
rules:
- apiGroups:
Expand All @@ -6494,7 +6494,7 @@ apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
component: conventions.carto.run
app.kubernetes.io/component: conventions
name: cartographer-conventions-leader-election-rolebinding
namespace: cartographer-system
roleRef:
Expand All @@ -6510,7 +6510,7 @@ apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
component: conventions.carto.run
app.kubernetes.io/component: conventions
name: cartographer-conventions-manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
Expand All @@ -6525,7 +6525,7 @@ apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
component: conventions.carto.run
app.kubernetes.io/component: conventions
name: cartographer-conventions-proxy-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
Expand All @@ -6542,7 +6542,7 @@ data:
kind: Secret
metadata:
labels:
component: conventions.carto.run
app.kubernetes.io/component: conventions
control-plane: controller-manager
name: cartographer-conventions-ca-certificates
namespace: cartographer-system
Expand All @@ -6556,7 +6556,7 @@ metadata:
prometheus.io/scheme: https
prometheus.io/scrape: "true"
labels:
component: conventions.carto.run
app.kubernetes.io/component: conventions
control-plane: controller-manager
name: cartographer-conventions-controller-manager-metrics-service
namespace: cartographer-system
Expand All @@ -6566,42 +6566,42 @@ spec:
port: 8443
targetPort: https
selector:
component: conventions.carto.run
app.kubernetes.io/component: conventions
control-plane: controller-manager
---
apiVersion: v1
kind: Service
metadata:
labels:
component: conventions.carto.run
app.kubernetes.io/component: conventions
name: cartographer-conventions-webhook-service
namespace: cartographer-system
spec:
ports:
- port: 443
targetPort: 9443
selector:
component: conventions.carto.run
app.kubernetes.io/component: conventions
control-plane: controller-manager
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
component: conventions.carto.run
app.kubernetes.io/component: conventions
control-plane: controller-manager
name: cartographer-conventions-controller-manager
namespace: cartographer-system
spec:
replicas: 1
selector:
matchLabels:
component: conventions.carto.run
app.kubernetes.io/component: conventions
control-plane: controller-manager
template:
metadata:
labels:
component: conventions.carto.run
app.kubernetes.io/component: conventions
control-plane: controller-manager
spec:
containers:
Expand Down Expand Up @@ -6664,7 +6664,7 @@ apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
labels:
component: conventions.carto.run
app.kubernetes.io/component: conventions
name: cartographer-conventions-serving-cert
namespace: cartographer-system
spec:
Expand All @@ -6681,7 +6681,7 @@ apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
labels:
component: conventions.carto.run
app.kubernetes.io/component: conventions
name: cartographer-conventions-selfsigned-issuer
namespace: cartographer-system
spec:
Expand All @@ -6694,7 +6694,7 @@ metadata:
cert-manager.io/inject-ca-from: cartographer-system/cartographer-conventions-serving-cert
admissions.enforcer/disabled: "true"
labels:
component: conventions.carto.run
app.kubernetes.io/component: conventions
name: cartographer-conventions-mutating-webhook-configuration
webhooks:
- admissionReviewVersions:
Expand Down Expand Up @@ -6745,7 +6745,7 @@ metadata:
cert-manager.io/inject-ca-from: cartographer-system/cartographer-conventions-serving-cert
admissions.enforcer/disabled: "true"
labels:
component: conventions.carto.run
app.kubernetes.io/component: conventions
name: cartographer-conventions-validating-webhook-configuration
webhooks:
- admissionReviewVersions:
Expand Down
16 changes: 16 additions & 0 deletions docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- [PodIntent (conventions.carto.run/v1alpha1)](#podintent-conventionscartorunv1alpha1)
- [ClusterPodConvention (conventions.carto.run/v1alpha1)](#clusterpodconvention-conventionscartorunv1alpha1)
- [PodConventionContext (webhooks.conventions.carto.run/v1alpha1)](#podconventioncontext-webhooksconventionscartorunv1alpha1)
- [Webhook contract](#webhook-contract)
- [Lifecycle](#lifecycle)
- [Security](#security)
- [Supportability](#supportability)
Expand Down Expand Up @@ -237,6 +238,21 @@ status: # the response

In the future other mechanisms may be defined to provide conventions other than webhooks. In particular, mechanisms that are safe to execute within the controller process like a YTT overlay or WebAssembly. Each mechanism will define the specifics of its own contract similar in scope to the `PodConventionContext`.

#### Webhook Contract
katmutua marked this conversation as resolved.
Show resolved Hide resolved

In order for the conventions controller to apply a set of conventions, it requires these conventions to be authored in the form of a webhook.

The authored webhook is registered as follows:
```
http.HandleFunc(< path >, webhook.ConventionHandler(ctx, conventions_name))
```
The convention controller handler function expects a `context Context` and a function describing conventions to be applied. See [springboot-convetions example](../samples/spring-convention-server/server.go)
```
http.HandleFunc("/", webhook.ConventionHandler(ctx, addSpringBootConventions))
```
The `Handler` is called from the controller in priority order as defined by the `ClusterPodConvention`.
The Open API Specification for the webhook is documented [here](/api/openapi-spec/conventions-server.yaml)

## Lifecycle

Cartographer Conventions lives entirely within the user space of a Kubernetes cluster. It can be installed, upgraded and removed like any other CRDs with a controller. Upgrade instructions will be included with each release.
Expand Down