Duck typing

Knative enables loose coupling of its components by using duck typing.

Duck typing means that the compatibility of a resource for use in a Knative system is determined by certain properties being present that can be used to identify the resource control plane shape and behaviors. These properties are based on a set of common definitions for different types of resources, called duck types.

If a resource has the same fields in the same schema locations as the common definition specifies, and the same control or data plane behaviors as the common definition specifies, Knative can use that resource as if it is the generic duck type, without specific knowledge about the resource type. Some resources may choose to opt-in to multiple duck types.

A fundamental use of duck typing in Knative is the use of object references in resource specs to point to another resource. The definition of the object containing the reference prescribes the expected duck type of the resource being referenced.

In the following example, a Knative Example resource named pointer references a Dog resource named pointee in its spec:

  1. apiVersion: sample.knative.dev/v1
  2. kind: Example
  3. metadata:
  4. name: pointer
  5. spec:
  6. size:
  7. apiVersion: extension.example.com/v1
  8. kind: Dog
  9. name: pointee

Assume the expected shape of a “Sizable” duck type is that in the status, we expect the following schema shape:

  1. <standard metadata>
  2. <spec ignored for Sizable>
  3. status:
  4. height: <in inches>
  5. weight: <in pounds>

Now the instance of pointee could look like this:

  1. apiVersion: extension.example.com/v1
  2. kind: Dog
  3. metadata:
  4. name: pointee
  5. spec:
  6. owner: Smith Family
  7. etc: more here
  8. status:
  9. lastFeeding: 2 hours ago
  10. hungry: true
  11. age: 2
  12. height: 27
  13. weight: 70

When the Example resource needs to do its work, it only acts on the information included in the “Sizable” duck type shape, and the Dog implementation is free to have the information that makes the most sense for that resource. The power of duck typing is apparent when we extend the system with a new type, say, Human. Assuming the new resource adheres to the contract set by the “Sizable”.

  1. apiVersion: sample.knative.dev/v1
  2. kind: Example
  3. metadata:
  4. name: pointer
  5. spec:
  6. size:
  7. apiVersion: people.example.com/v1
  8. kind: human
  9. name: pointee
  10. ---
  11. apiVersion: people.example.com/v1
  12. kind: Human
  13. metadata:
  14. name: pointee
  15. spec:
  16. etc: even more here
  17. status:
  18. college: true
  19. hungry: true
  20. age: 22
  21. height: 62
  22. weight: 120

The Example resource was able to do the logic it is set to do without explicit knowlage of Human or Dog.

Knative Duck Types

Knative defines several duck type contracts that are in use across the project:

Addressable

Addressable is expected to be the following shape:

  1. apiVersion: group/version
  2. kind: Kind
  3. status:
  4. address:
  5. url: http://host/path?query

Binding

Binding is expected to be in the following shape:

(with direct subject)

  1. apiVersion: group/version
  2. kind: Kind
  3. spec:
  4. subject:
  5. apiVersion: group/version
  6. kind: SomeKind
  7. namespace: the-namespace
  8. name: a-name

(with indirect subject)

  1. apiVersion: group/version
  2. kind: Kind
  3. spec:
  4. subject:
  5. apiVersion: group/version
  6. kind: SomeKind
  7. namespace: the-namespace
  8. selector:
  9. matchLabels:
  10. key: value

Source

Source is expected to be in the following shape:

(with ref sink)

  1. apiVersion: group/version
  2. kind: Kind
  3. spec:
  4. sink:
  5. ref:
  6. apiVersion: group/version
  7. kind: AnAddressableKind
  8. name: a-name
  9. ceOverrides:
  10. extensions:
  11. key: value
  12. status:
  13. observedGeneration: 1
  14. conditions:
  15. - type: Ready
  16. status: "True"
  17. sinkUri: http://host

(with uri sink)

  1. apiVersion: group/version
  2. kind: Kind
  3. spec:
  4. sink:
  5. uri: http://host/path?query
  6. ceOverrides:
  7. extensions:
  8. key: value
  9. status:
  10. observedGeneration: 1
  11. conditions:
  12. - type: Ready
  13. status: "True"
  14. sinkUri: http://host/path?query

(with ref and uri sink)

  1. apiVersion: group/version
  2. kind: Kind
  3. spec:
  4. sink:
  5. ref:
  6. apiVersion: group/version
  7. kind: AnAddressableKind
  8. name: a-name
  9. uri: /path?query
  10. ceOverrides:
  11. extensions:
  12. key: value
  13. status:
  14. observedGeneration: 1
  15. conditions:
  16. - type: Ready
  17. status: "True"
  18. sinkUri: http://host/path?query