Skip to content

Commit

Permalink
internal/annotation: Refactor annotations code from internal/dag
Browse files Browse the repository at this point in the history
Updates projectcontour#2388
Update projectcontour#403

Refactor annotations code from `internal/dag` into `internal/annotation` so that
code dealing with ingress status will be able to use it.

This includes moving some generic Kubernetes structs (`Object` and `Meta`) into
`k8s` from `dag`. `Meta` has also been renamed to `FullName` for greater clarity.

Signed-off-by: Nick Young <ynick@vmware.com>
  • Loading branch information
youngnick committed Apr 3, 2020
1 parent 74038d2 commit bfc6c70
Show file tree
Hide file tree
Showing 16 changed files with 491 additions and 417 deletions.
3 changes: 2 additions & 1 deletion cmd/contour/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"syscall"
"time"

"github.com/projectcontour/contour/internal/annotation"
"github.com/projectcontour/contour/internal/contour"
"github.com/projectcontour/contour/internal/dag"
"github.com/projectcontour/contour/internal/debug"
Expand Down Expand Up @@ -165,7 +166,7 @@ func doServe(log logrus.FieldLogger, ctx *serveContext) error {
HTTPSAccessLog: ctx.httpsAccessLog,
AccessLogType: ctx.AccessLogFormat,
AccessLogFields: ctx.AccessLogFields,
MinimumProtocolVersion: dag.MinProtoVersion(ctx.TLSConfig.MinimumProtocolVersion),
MinimumProtocolVersion: annotation.MinProtoVersion(ctx.TLSConfig.MinimumProtocolVersion),
RequestTimeout: ctx.RequestTimeout,
},
ListenerCache: contour.NewListenerCache(ctx.statsAddr, ctx.statsPort),
Expand Down
83 changes: 53 additions & 30 deletions internal/dag/annotations.go → internal/annotation/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package dag
package annotation

import (
"fmt"
Expand All @@ -20,10 +20,12 @@ import (
"time"

envoy_api_v2_auth "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth"
"github.com/projectcontour/contour/internal/k8s"
"k8s.io/api/networking/v1beta1"
)

func annotationIsKnown(key string) bool {
// IsKnown checks if an annotation is one Contour knows about.
func IsKnown(key string) bool {
// We should know about everything with a Contour prefix.
if strings.HasPrefix(key, "projectcontour.io/") ||
strings.HasPrefix(key, "contour.heptio.com/") {
Expand Down Expand Up @@ -77,7 +79,8 @@ var annotationsByKind = map[string]map[string]struct{}{
},
}

func validAnnotationForKind(kind string, key string) bool {
// ValidForKind checks if a particular annotation is valid for a given Kind.
func ValidForKind(kind string, key string) bool {
if a, ok := annotationsByKind[kind]; ok {
// Canonicalize the name while we still have legacy support.
key = strings.Replace(key, "contour.heptio.com/", "projectcontour.io/", -1)
Expand All @@ -95,10 +98,10 @@ func validAnnotationForKind(kind string, key string) bool {
return true
}

// compatAnnotation checks the Object for the given annotation, first with the
// CompatAnnotation checks the Object for the given annotation, first with the
// "projectcontour.io/" prefix, and then with the "contour.heptio.com/" prefix
// if that is not found.
func compatAnnotation(o Object, key string) string {
func CompatAnnotation(o k8s.Object, key string) string {
a := o.GetObjectMeta().GetAnnotations()

if val, ok := a["projectcontour.io/"+key]; ok {
Expand All @@ -108,7 +111,7 @@ func compatAnnotation(o Object, key string) string {
return a["contour.heptio.com/"+key]
}

// parseUInt32 parses the supplied string as if it were a uint32.
// ParseUInt32 parses the supplied string as if it were a uint32.
// If the value is not present, or malformed, or outside uint32's range, zero is returned.
func parseUInt32(s string) uint32 {
v, err := strconv.ParseUint(s, 10, 32)
Expand All @@ -118,10 +121,10 @@ func parseUInt32(s string) uint32 {
return uint32(v)
}

// parseUpstreamProtocols parses the annotations map for contour.heptio.com/upstream-protocol.{protocol}
// ParseUpstreamProtocols parses the annotations map for contour.heptio.com/upstream-protocol.{protocol}
// and projectcontour.io/upstream-protocol.{protocol} annotations.
// 'protocol' identifies which protocol must be used in the upstream.
func parseUpstreamProtocols(m map[string]string) map[string]string {
func ParseUpstreamProtocols(m map[string]string) map[string]string {
annotations := []string{
"contour.heptio.com/upstream-protocol",
"projectcontour.io/upstream-protocol",
Expand All @@ -142,19 +145,21 @@ func parseUpstreamProtocols(m map[string]string) map[string]string {
return up
}

// httpAllowed returns true unless the kubernetes.io/ingress.allow-http annotation is
// HTTPAllowed returns true unless the kubernetes.io/ingress.allow-http annotation is
// present and set to false.
func httpAllowed(i *v1beta1.Ingress) bool {
func HTTPAllowed(i *v1beta1.Ingress) bool {
return !(i.Annotations["kubernetes.io/ingress.allow-http"] == "false")
}

// tlsRequired returns true if the ingress.kubernetes.io/force-ssl-redirect annotation is
// TLSRequired returns true if the ingress.kubernetes.io/force-ssl-redirect annotation is
// present and set to true.
func tlsRequired(i *v1beta1.Ingress) bool {
func TLSRequired(i *v1beta1.Ingress) bool {
return i.Annotations["ingress.kubernetes.io/force-ssl-redirect"] == "true"
}

func websocketRoutes(i *v1beta1.Ingress) map[string]bool {
// WebsocketRoutes retrieves the details of routes that should have websockets enabled from the
// associated websocket-routes annotation.
func WebsocketRoutes(i *v1beta1.Ingress) map[string]bool {
routes := make(map[string]bool)
for _, v := range strings.Split(i.Annotations["projectcontour.io/websocket-routes"], ",") {
route := strings.TrimSpace(v)
Expand All @@ -171,23 +176,23 @@ func websocketRoutes(i *v1beta1.Ingress) map[string]bool {
return routes
}

// numRetries returns the number of retries specified by the "contour.heptio.com/num-retries"
// NumRetries returns the number of retries specified by the "contour.heptio.com/num-retries"
// or "projectcontour.io/num-retries" annotation.
func numRetries(i *v1beta1.Ingress) uint32 {
return parseUInt32(compatAnnotation(i, "num-retries"))
func NumRetries(i *v1beta1.Ingress) uint32 {
return parseUInt32(CompatAnnotation(i, "num-retries"))
}

// perTryTimeout returns the duration envoy will wait per retry cycle.
func perTryTimeout(i *v1beta1.Ingress) time.Duration {
return parseTimeout(compatAnnotation(i, "per-try-timeout"))
// PerTryTimeout returns the duration envoy will wait per retry cycle.
func PerTryTimeout(i *v1beta1.Ingress) time.Duration {
return k8s.ParseTimeout(CompatAnnotation(i, "per-try-timeout"))
}

// ingressClass returns the first matching ingress class for the following
// IngressClass returns the first matching ingress class for the following
// annotations:
// 1. projectcontour.io/ingress.class
// 2. contour.heptio.com/ingress.class
// 3. kubernetes.io/ingress.class
func ingressClass(o Object) string {
func IngressClass(o k8s.Object) string {
a := o.GetObjectMeta().GetAnnotations()
if class, ok := a["projectcontour.io/ingress.class"]; ok {
return class
Expand Down Expand Up @@ -215,24 +220,42 @@ func MinProtoVersion(version string) envoy_api_v2_auth.TlsParameters_TlsProtocol
}
}

// maxConnections returns the value of the first matching max-connections
// MaxConnections returns the value of the first matching max-connections
// annotation for the following annotations:
// 1. projectcontour.io/max-connections
// 2. contour.heptio.com/max-connections
//
// '0' is returned if the annotation is absent or unparseable.
func maxConnections(o Object) uint32 {
return parseUInt32(compatAnnotation(o, "max-connections"))
func MaxConnections(o k8s.Object) uint32 {
return parseUInt32(CompatAnnotation(o, "max-connections"))
}

func maxPendingRequests(o Object) uint32 {
return parseUInt32(compatAnnotation(o, "max-pending-requests"))
// MaxPendingRequests returns the value of the first matching max-pending-requests
// annotation for the following annotations:
// 1. projectcontour.io/max-pending-requests
// 2. contour.heptio.com/max-pending-requests
//
// '0' is returned if the annotation is absent or unparseable.
func MaxPendingRequests(o k8s.Object) uint32 {
return parseUInt32(CompatAnnotation(o, "max-pending-requests"))
}

func maxRequests(o Object) uint32 {
return parseUInt32(compatAnnotation(o, "max-requests"))
// MaxRequests returns the value of the first matching max-requests
// annotation for the following annotations:
// 1. projectcontour.io/max-requests
// 2. contour.heptio.com/max-requests
//
// '0' is returned if the annotation is absent or unparseable.
func MaxRequests(o k8s.Object) uint32 {
return parseUInt32(CompatAnnotation(o, "max-requests"))
}

func maxRetries(o Object) uint32 {
return parseUInt32(compatAnnotation(o, "max-retries"))
// MaxRetries returns the value of the first matching max-retries
// annotation for the following annotations:
// 1. projectcontour.io/max-retries
// 2. contour.heptio.com/max-retries
//
// '0' is returned if the annotation is absent or unparseable.
func MaxRetries(o k8s.Object) uint32 {
return parseUInt32(CompatAnnotation(o, "max-retries"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package dag
package annotation

import (
"fmt"
"testing"

"github.com/projectcontour/contour/internal/k8s"

ingressroutev1 "github.com/projectcontour/contour/apis/contour/v1beta1"
projectcontour "github.com/projectcontour/contour/apis/projectcontour/v1"
"github.com/projectcontour/contour/internal/assert"
"github.com/projectcontour/contour/internal/k8s"
v1 "k8s.io/api/core/v1"
"k8s.io/api/networking/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -122,7 +121,7 @@ func TestParseUpstreamProtocols(t *testing.T) {

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
got := parseUpstreamProtocols(tc.a)
got := ParseUpstreamProtocols(tc.a)
assert.Equal(t, tc.want, got)
})
}
Expand Down Expand Up @@ -267,7 +266,7 @@ func TestWebsocketRoutes(t *testing.T) {

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
got := websocketRoutes(tc.a)
got := WebsocketRoutes(tc.a)
assert.Equal(t, tc.want, got)
})
}
Expand Down Expand Up @@ -317,7 +316,7 @@ func TestHttpAllowed(t *testing.T) {

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
got := httpAllowed(tc.i)
got := HTTPAllowed(tc.i)
want := tc.valid
if got != want {
t.Fatalf("got: %v, want: %v", got, want)
Expand Down Expand Up @@ -374,7 +373,7 @@ func TestAnnotationCompat(t *testing.T) {

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
got := compatAnnotation(tc.svc, "annotation")
got := CompatAnnotation(tc.svc, "annotation")
want := tc.value
if got != want {
t.Fatalf("got: %v, want: %v", got, want)
Expand All @@ -389,7 +388,7 @@ func TestAnnotationKindValidation(t *testing.T) {
valid bool
}
tests := map[string]struct {
obj Object
obj k8s.Object
annotations map[string]status
}{
"service": {
Expand Down Expand Up @@ -445,8 +444,8 @@ func TestAnnotationKindValidation(t *testing.T) {
for key := range annotationsByKind[kind] {
t.Run(fmt.Sprintf("%s is known and valid for %s", key, kind),
func(t *testing.T) {
assert.Equal(t, true, annotationIsKnown(key))
assert.Equal(t, true, validAnnotationForKind(kind, key))
assert.Equal(t, true, IsKnown(key))
assert.Equal(t, true, ValidForKind(kind, key))
})
}
}
Expand All @@ -455,9 +454,16 @@ func TestAnnotationKindValidation(t *testing.T) {
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
for k, s := range tc.annotations {
assert.Equal(t, s.known, annotationIsKnown(k))
assert.Equal(t, s.valid, validAnnotationForKind(k8s.KindOf(tc.obj), k))
assert.Equal(t, s.known, IsKnown(k))
assert.Equal(t, s.valid, ValidForKind(k8s.KindOf(tc.obj), k))
}
})
}
}

func backend(name string, port intstr.IntOrString) *v1beta1.IngressBackend {
return &v1beta1.IngressBackend{
ServiceName: name,
ServicePort: port,
}
}
2 changes: 1 addition & 1 deletion internal/contour/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ func (e *EventHandler) updateDAG() {
}

// setStatus updates the status of objects.
func (e *EventHandler) setStatus(statuses map[dag.Meta]dag.Status) {
func (e *EventHandler) setStatus(statuses map[k8s.FullName]dag.Status) {
for _, st := range statuses {
switch obj := st.Object.(type) {
case *ingressroutev1.IngressRoute:
Expand Down
2 changes: 1 addition & 1 deletion internal/contour/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (e *EventRecorder) recordOperation(op string, obj interface{}) {
e.Counter.WithLabelValues(op, kind).Inc()
}

func calculateRouteMetric(statuses map[dag.Meta]dag.Status) (metrics.RouteMetric, metrics.RouteMetric) {
func calculateRouteMetric(statuses map[k8s.FullName]dag.Status) (metrics.RouteMetric, metrics.RouteMetric) {
irMetricTotal := make(map[metrics.Meta]int)
irMetricValid := make(map[metrics.Meta]int)
irMetricInvalid := make(map[metrics.Meta]int)
Expand Down

0 comments on commit bfc6c70

Please sign in to comment.