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

kuma-cp: validate value of protocol tag on a Dataplane resource #576

Merged
merged 1 commit into from
Feb 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

Changes:

* feature: validate value of `protocol` tag on a Dataplane resource
[#576](https://github.com/Kong/kuma/pull/576)
* feature: support `<port>.service.kuma.io/protocol` annotation on k8s as a way for users to indicate protocol of a service
[#575](https://github.com/Kong/kuma/pull/575)
* feature: generate HTTP-specific inbound listeners for services tagged with `protocol: http`
Expand Down
17 changes: 17 additions & 0 deletions pkg/core/resources/apis/mesh/dataplane_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,23 @@ func ParseProtocol(tag string) Protocol {
}
}

// ProtocolList represents a list of Protocols.
type ProtocolList []Protocol

func (l ProtocolList) Strings() []string {
values := make([]string, len(l))
for i := range l {
values[i] = string(l[i])
}
return values
}

// SupportedProtocols is a list of supported protocols that will be communicated to a user.
var SupportedProtocols = ProtocolList{
ProtocolHTTP,
ProtocolTCP,
}

var ipv4loopback = net.IPv4(127, 0, 0, 1)

func (d *DataplaneResource) UsesInterface(address net.IP, port uint32) bool {
Expand Down
7 changes: 7 additions & 0 deletions pkg/core/resources/apis/mesh/dataplane_validator.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package mesh

import (
"fmt"

mesh_proto "github.com/Kong/kuma/api/mesh/v1alpha1"
"github.com/Kong/kuma/pkg/core/validators"
)
Expand Down Expand Up @@ -43,6 +45,11 @@ func validateInbound(inbound *mesh_proto.Dataplane_Networking_Inbound) validator
if _, exist := inbound.Tags[mesh_proto.ServiceTag]; !exist {
result.AddViolationAt(validators.RootedAt("tags").Key(mesh_proto.ServiceTag), `tag has to exist`)
}
if value, exist := inbound.Tags[mesh_proto.ProtocolTag]; exist {
if ParseProtocol(value) == ProtocolUnknown {
result.AddViolationAt(validators.RootedAt("tags").Key(mesh_proto.ProtocolTag), fmt.Sprintf("tag %q has an invalid value %q. %s", mesh_proto.ProtocolTag, value, AllowedValuesHint(SupportedProtocols.Strings()...)))
}
}
for name, value := range inbound.Tags {
if value == "" {
result.AddViolationAt(validators.RootedAt("tags").Key(name), `tag value cannot be empty`)
Expand Down
40 changes: 40 additions & 0 deletions pkg/core/resources/apis/mesh/dataplane_validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ var _ = Describe("Dataplane", func() {
Entry("dataplane with inbounds", func() core_mesh.DataplaneResource {
return validDataplane
}),
Entry("dataplane with inbounds and no `protocol` tag", func() core_mesh.DataplaneResource {
delete(validDataplane.Spec.Networking.Inbound[0].Tags, "protocol")
return validDataplane
}),
Entry("dataplane with inbounds and a valid `protocol` tag", func() core_mesh.DataplaneResource {
validDataplane.Spec.Networking.Inbound[0].Tags["protocol"] = "http"
return validDataplane
}),
Entry("dataplane with gateway", func() core_mesh.DataplaneResource {
return core_mesh.DataplaneResource{
Meta: &model.ResourceMeta{
Expand Down Expand Up @@ -183,6 +191,38 @@ var _ = Describe("Dataplane", func() {
},
},
}),
Entry("inbound: `protocol` tag with an empty value", testCase{
dataplane: func() core_mesh.DataplaneResource {
validDataplane.Spec.Networking.Inbound[0].Tags["protocol"] = ""
return validDataplane
},
validationResult: &validators.ValidationError{
Violations: []validators.Violation{
{
Field: `networking.inbound[0].tags["protocol"]`,
Message: `tag "protocol" has an invalid value "". Allowed values: http, tcp`,
},
{
Field: `networking.inbound[0].tags["protocol"]`,
Message: `tag value cannot be empty`,
},
},
},
}),
Entry("inbound: `protocol` tag with unsupported value", testCase{
dataplane: func() core_mesh.DataplaneResource {
validDataplane.Spec.Networking.Inbound[0].Tags["protocol"] = "not-yet-supported-protocol"
return validDataplane
},
validationResult: &validators.ValidationError{
Violations: []validators.Violation{
{
Field: `networking.inbound[0].tags["protocol"]`,
Message: `tag "protocol" has an invalid value "not-yet-supported-protocol". Allowed values: http, tcp`,
},
},
},
}),
Entry("gateway: empty service tag", testCase{
dataplane: func() core_mesh.DataplaneResource {
validDataplane.Spec.Networking.Inbound = nil
Expand Down
9 changes: 9 additions & 0 deletions pkg/core/resources/apis/mesh/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package mesh
import (
"fmt"
"sort"
"strings"

mesh_proto "github.com/Kong/kuma/api/mesh/v1alpha1"
"github.com/Kong/kuma/pkg/core/validators"
Expand Down Expand Up @@ -122,3 +123,11 @@ func ValidateThreshold(path validators.PathBuilder, threshold uint32) (err valid
}
return
}

func AllowedValuesHint(values ...string) string {
options := strings.Join(values, ", ")
if len(values) == 0 {
options = "(none)"
}
return fmt.Sprintf("Allowed values: %s", options)
}
39 changes: 39 additions & 0 deletions pkg/core/resources/apis/mesh/validators_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package mesh_test

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"

. "github.com/Kong/kuma/pkg/core/resources/apis/mesh"
)

var _ = Describe("AllowedValuesHint()", func() {

type testCase struct {
values []string
expected string
}

DescribeTable("should generate a proper hint",
func(given testCase) {
Expect(AllowedValuesHint(given.values...)).To(Equal(given.expected))
},
Entry("nil list", testCase{
values: nil,
expected: `Allowed values: (none)`,
}),
Entry("empty list", testCase{
values: []string{},
expected: `Allowed values: (none)`,
}),
Entry("one-item list", testCase{
values: []string{"http"},
expected: `Allowed values: http`,
}),
Entry("multi-item list", testCase{
values: []string{"grpc", "http", "http2", "mongo", "mysql", "redis", "tcp"},
expected: `Allowed values: grpc, http, http2, mongo, mysql, redis, tcp`,
}),
)
})