---
title: "Defining a custom resource definition as the source"
linkTitle: "Defining a CRD source"
weight: 20
type: "docs"
---

We'll use Kubebuilder to generate a new
[Kubernetes Custom Resource Definition](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/)
for our source. Check out the [Kubebuilder book](https://book.kubebuilder.io/)
to learn more about Kubebuilder.

## Create the Source CRD Skeleton

_When in doubt, the
[Kubebuilder Quick Start docs](https://book.kubebuilder.io/quick-start.html) are
likely more current than these instructions._

Use the `kubebuilder create api` command to create the API definition for a new
CRD and a controller to reconcile it.

You'll need to choose the following:

- **A group name.** This is the resource group that will contain the resource.
  It's prepended to the domain name chosen earlier to produce the
  fully-qualified resource name. The reference project uses `sources`.
- **A version name.** This is the initial version string for the CRD. It's
  usually `v1alpha1` for new resources. The reference project uses `v1alpha1`.
- **A kind name.** This is the unqualified type name of the resource. The
  reference project uses `SampleSource`.

  The fully-qualified name of the reference resource is
  `samplesources.sources.knative.dev`, and its `apiVersion` is
  `sources.knative.dev/v1alpha1`.

```sh
kubebuilder create api --group sources --version v1alpha1 --kind SampleSource
```

This command will ask if you want to create the resource and controller. Answer
yes to both to generate scaffolding to edit in future steps.

The result of this command in the reference project can be viewed at
https://github.com/grantr/sample-source/pull/2.

## Add Fields to Source Type Definition

<!-- TODO explain why there are Sink and SinkURI fields -->

Locate the `Spec` and `Status` types generated by Kubebuilder. In the reference
project, these types are defined in
`pkg/apis/sources/v1alpha1/samplesource_types.go`.

Add a `Sink` field to the `Spec` type. Its type should be
`*corev1.ObjectReference`. You'll also need to import the Go package
`k8s.io/api/core/v1` as `corev1`.

```go
type SampleSourceSpec struct {
    // Sink is a reference to an object that will resolve to a domain name to use
    // as the sink.
    // +optional
    Sink *corev1.ObjectReference `json:"sink,omitempty"`
}
```

Add a `SinkURI` string field to the `Status` type.

```go
type SampleSourceStatus struct {
    // SinkURI is the current active sink URI that has been configured for the SampleSource.
    // +optional
    SinkURI string `json:"sinkURI,omitempty"`
}
```

Run `dep ensure` to add the new library to `vendor` if necessary.

```sh
dep ensure
```

Run `make` to regenerate the deepcopy code in `zz_generated.deepcopy.go` and the
CRD's OpenAPI schema.

```sh
make
```

## Update type tests

Locate the type tests file. In the reference project, this is
`samplesource_types_test.go` in the same directory as `samplesource_types.go`.

Edit the `created` object to include a `Spec` declaring a `Sink`.

```go
created := &SampleSource{
  ObjectMeta: metav1.ObjectMeta{
    Name:      "foo",
    Namespace: "default",
  },
  Spec: SampleSourceSpec{
    Sink: &corev1.ObjectReference{
      Name:       "fooservice",
      APIVersion: "v1",
      Kind:       "Service",
    },
  },
}
```

Edit the `updated` object, setting `SinkURI` in its `Status`.

```go
// Test Updating the Labels and Status
updated := fetched.DeepCopy()
updated.Labels = map[string]string{"hello": "world"}
updated.Status = SampleSourceStatus{
  SinkURI: "http://example.com",
}
```

Run `make` to run tests and calculate the coverage percentage for all packages
in `pkg` and `cmd`.

```sh
make
```

```
go test ./pkg/... ./cmd/... -coverprofile cover.out
?       github.com/knative/sample-source/pkg/apis   [no test files]
?       github.com/knative/sample-source/pkg/apis/sources   [no test files]
ok      github.com/knative/sample-source/pkg/apis/sources/v1alpha1  8.528s  coverage: 33.3% of statements
?       github.com/knative/sample-source/pkg/controller [no test files]
ok      github.com/knative/sample-source/pkg/controller/samplesource    9.151s  coverage: 67.6% of statements
?       github.com/knative/sample-source/pkg/webhook    [no test files]
?       github.com/knative/sample-source/cmd/manager    [no test files]
```

These edits in the reference project can be viewed at
https://github.com/grantr/sample-source/pull/3.

## Enable the Status subresource

The Status subresource allows controllers to update only the Status field
without updating the Spec field. Since controllers should not update the Spec of
their watched objects, this makes controllers safer and easier to write by
ensuring they never update Spec accidentally.

_This feature became beta in Kubernetes 1.11, so it might not work properly with
older versions._

Add the Kubebuilder status subresource annotation to the struct defining the
object. In the reference project, this is in
`pkg/apis/sources/v1alpha1/samplesource_types.go`.

```go
// SampleSource is the Schema for the samplesources API
// +k8s:openapi-gen=true
// +kubebuilder:subresource:status
type SampleSource struct {
  // ...
}
```

Run `make` to regenerate the CRD yaml.

```sh
make
```

## Update tests to use the Status subresource

Now update the tests to use the Status subresource to set SinkURI. This now
needs to be separate from the labels update.

```go
// Test Updating the Labels
updated := fetched.DeepCopy()
updated.Labels = map[string]string{"hello": "world"}
g.Expect(c.Update(context.TODO(), updated)).NotTo(gomega.HaveOccurred())
g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred())
g.Expect(fetched).To(gomega.Equal(updated))

// Test Updating the Status via subresource
updated.Status = SampleSourceStatus{
    SinkURI: "http://example.com",
}
// DeepCopy is required here because the Status update will set the SelfLink
// to reference to /status subresource instead of the original resource.
statusupdated := updated.DeepCopy()
g.Expect(c.Status().Update(context.TODO(), statusupdated)).NotTo(gomega.HaveOccurred())
g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred())
g.Expect(fetched.Status).To(gomega.Equal(updated.Status))
```

Run `make` to verify tests pass.

```sh
make
```

These edits in the reference project can be viewed at
https://github.com/grantr/sample-source/pull/4.

Next: [Reconcile Sources](./03-reconcile-sources.md)
