Skip to content

Commit

Permalink
move expose NEG logic into NEG package
Browse files Browse the repository at this point in the history
  • Loading branch information
agau4779 committed Jun 19, 2018
1 parent 0b7923f commit c89571a
Show file tree
Hide file tree
Showing 9 changed files with 352 additions and 151 deletions.
106 changes: 21 additions & 85 deletions pkg/annotations/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"fmt"

"k8s.io/api/core/v1"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/ingress-gce/pkg/flags"
)

Expand Down Expand Up @@ -86,40 +85,11 @@ type Service struct {
v map[string]string
}

// PortNameMap is a map of ServicePort:TargetPort.
type PortNameMap map[int32]string

// FromService extracts the annotations from an Service definition.
func FromService(obj *v1.Service) *Service {
return &Service{obj.Annotations}
}

// Union returns the union of the Port:TargetPort mappings
func (p1 PortNameMap) Union(p2 PortNameMap) PortNameMap {
result := make(PortNameMap)
for svcPort, targetPort := range p1 {
result[svcPort] = targetPort
}

for svcPort, targetPort := range p2 {
result[svcPort] = targetPort
}

return result
}

// Difference returns the set of Port:TargetPorts in p2 that aren't present in p1
func (p1 PortNameMap) Difference(p2 PortNameMap) PortNameMap {
result := make(PortNameMap)
for svcPort, targetPort := range p1 {
if _, ok := p2[svcPort]; !ok {
result[svcPort] = targetPort
}
}

return result
}

// ApplicationProtocols returns a map of port (name or number) to the protocol
// on the port.
func (svc *Service) ApplicationProtocols() (map[string]AppProtocol, error) {
Expand Down Expand Up @@ -170,6 +140,27 @@ func (svc *Service) NEGExposed() bool {
return ok && len(v) > 0
}

var (
ErrExposeNegAnnotationMissing = errors.New("No NEG ServicePorts specified")
ErrExposeNegAnnotationInvalid = errors.New("Expose NEG annotation is invalid")
)

// ExposeNegAnnotation returns the value of the Expose NEG annotation key
func (svc *Service) ExposeNegAnnotation() (ExposeNegAnnotation, error) {
annotation, ok := svc.v[ExposeNEGAnnotationKey]
if !ok {
return nil, ErrExposeNegAnnotationMissing
}

// TODO: add link to Expose NEG documentation when complete
var exposedNegPortMap ExposeNegAnnotation
if err := json.Unmarshal([]byte(annotation), &exposedNegPortMap); err != nil {
return nil, ErrExposeNegAnnotationInvalid
}

return exposedNegPortMap, nil
}

// NEGEnabled is true if the service uses NEGs.
func (svc *Service) NEGEnabled() bool {
return svc.NEGExposed() || svc.NEGEnabledForIngress()
Expand All @@ -196,58 +187,3 @@ func (svc *Service) GetBackendConfigs() (*BackendConfigs, error) {
}
return &configs, nil
}

var (
ErrExposeNegAnnotationMissing = errors.New("No NEG ServicePorts specified")
ErrExposeNegAnnotationInvalid = errors.New("Expose NEG annotation is invalid")
)

// NEGServicePorts returns the parsed ServicePorts from the annotation.
// knownPorts represents the known Port:TargetPort attributes of servicePorts
// that already exist on the service. This function returns an error if
// any of the parsed ServicePorts from the annotation is not in knownPorts.
func (svc *Service) NEGServicePorts(knownPorts PortNameMap) (PortNameMap, error) {
v, ok := svc.v[ExposeNEGAnnotationKey]
if !ok {
return nil, ErrExposeNegAnnotationMissing
}

// TODO: add link to Expose NEG documentation when complete
var exposedNegPortMap ExposeNegAnnotation
if err := json.Unmarshal([]byte(v), &exposedNegPortMap); err != nil {
return nil, ErrExposeNegAnnotationInvalid
}

portSet := make(PortNameMap)
var errList []error
for port, _ := range exposedNegPortMap {
// TODO: also validate ServicePorts in the exposed NEG annotation via webhook
if _, ok := knownPorts[port]; !ok {
errList = append(errList, fmt.Errorf("ServicePort %v doesn't exist on Service", port))
}
portSet[port] = knownPorts[port]
}

return portSet, utilerrors.NewAggregate(errList)
}

// NegServiceState contains name and zone of the Network Endpoint Group
// resources associated with this service
type NegServiceState struct {
// NetworkEndpointGroups returns the mapping between service port and NEG
// resource. key is service port, value is the name of the NEG resource.
NetworkEndpointGroups PortNameMap `json:"network_endpoint_groups,omitempty"`
Zones []string `json:"zones,omitempty"`
}

// GenNegServiceState generates a NegServiceState denoting the current NEGs
// associated with the given ports.
// NetworkEndpointGroups is a mapping between ServicePort and NEG name
// Zones is a list of zones where the NEGs exist.
func GenNegServiceState(zones []string, portToNegs PortNameMap) NegServiceState {
res := NegServiceState{}
res.NetworkEndpointGroups = make(PortNameMap)
res.Zones = zones
res.NetworkEndpointGroups = portToNegs
return res
}
54 changes: 20 additions & 34 deletions pkg/annotations/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,11 @@ limitations under the License.
package annotations

import (
"fmt"
"reflect"
"testing"

"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/ingress-gce/pkg/flags"
)

Expand Down Expand Up @@ -264,13 +262,12 @@ func TestBackendConfigs(t *testing.T) {
}
}

func TestNEGServicePorts(t *testing.T) {
func TestExposeNegAnnotation(t *testing.T) {
testcases := []struct {
desc string
annotation string
knownPortMap PortNameMap
expectedPortMap PortNameMap
expectedErr error
desc string
annotation string
expected ExposeNegAnnotation
expectedErr error
}{
{
desc: "no expose NEG annotation",
Expand All @@ -283,26 +280,15 @@ func TestNEGServicePorts(t *testing.T) {
expectedErr: ErrExposeNegAnnotationInvalid,
},
{
desc: "NEG annotation references port that Service does not have",
annotation: `{"3000":{}}`,
expectedErr: utilerrors.NewAggregate([]error{
fmt.Errorf("ServicePort %v doesn't exist on Service", 3000),
}),
knownPortMap: PortNameMap{80: "some_port", 443: "another_port"},
expectedPortMap: PortNameMap{3000: ""},
},
{
desc: "NEG annotation references existing service ports",
annotation: `{"80":{},"443":{}}`,
knownPortMap: PortNameMap{80: "namedport", 443: "3000"},
expectedPortMap: PortNameMap{80: "namedport", 443: "3000"},
desc: "NEG annotation references existing service ports",
expected: ExposeNegAnnotation{80: NegAttributes{}, 443: NegAttributes{}},
annotation: `{"80":{},"443":{}}`,
},

{
desc: "NEGServicePort takes the union of known ports and ports referenced in the annotation",
annotation: `{"80":{}}`,
knownPortMap: PortNameMap{80: "8080", 3000: "3030", 4000: "4040"},
expectedPortMap: PortNameMap{80: "8080"},
desc: "NEGServicePort takes the union of known ports and ports referenced in the annotation",
annotation: `{"80":{}}`,
expected: ExposeNegAnnotation{80: NegAttributes{}},
},
}

Expand All @@ -313,23 +299,23 @@ func TestNEGServicePorts(t *testing.T) {
},
}

if len(tc.annotation) > 0 {
service.Annotations[ExposeNEGAnnotationKey] = tc.annotation
}
t.Run(tc.desc, func(t *testing.T) {
if len(tc.annotation) > 0 {
service.Annotations[ExposeNEGAnnotationKey] = tc.annotation
}

svc := FromService(service)
svc := FromService(service)
exposeNegStruct, err := svc.ExposeNegAnnotation()

t.Run(tc.desc, func(t *testing.T) {
svcPorts, err := svc.NEGServicePorts(tc.knownPortMap)
if tc.expectedErr == nil && err != nil {
t.Errorf("ExpectedNEGServicePorts to not return an error, got: %v", err)
}

if !reflect.DeepEqual(svcPorts, tc.expectedPortMap) {
t.Errorf("Expected NEGServicePorts to equal: %v; got: %v", tc.expectedPortMap, svcPorts)
if !reflect.DeepEqual(exposeNegStruct, tc.expected) {
t.Errorf("Expected NEGServicePorts to equal: %v; got: %v", tc.expected, exposeNegStruct)
}

if tc.expectedErr != nil && err.Error() != tc.expectedErr.Error() {
if tc.expectedErr != nil && err != tc.expectedErr {
t.Errorf("Expected NEGServicePorts to return a %v error, got: %v", tc.expectedErr, err)
}
})
Expand Down
92 changes: 92 additions & 0 deletions pkg/neg/annotations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package neg

import (
"fmt"

utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/ingress-gce/pkg/annotations"
)

// PortNameMap is a map of ServicePort:TargetPort.
type PortNameMap map[int32]string

// Union returns the union of the Port:TargetPort mappings
func (p1 PortNameMap) Union(p2 PortNameMap) PortNameMap {
result := make(PortNameMap)
for svcPort, targetPort := range p1 {
result[svcPort] = targetPort
}

for svcPort, targetPort := range p2 {
result[svcPort] = targetPort
}

return result
}

// Difference returns the set of Port:TargetPorts in p2 that aren't present in p1
func (p1 PortNameMap) Difference(p2 PortNameMap) PortNameMap {
result := make(PortNameMap)
for svcPort, targetPort := range p1 {
if _, ok := p2[svcPort]; !ok {
result[svcPort] = targetPort
}
}

return result
}

// NEGServicePorts returns the parsed ServicePorts from the annotation.
// knownPorts represents the known Port:TargetPort attributes of servicePorts
// that already exist on the service. This function returns an error if
// any of the parsed ServicePorts from the annotation is not in knownPorts.
func NEGServicePorts(exposedNegPortMap annotations.ExposeNegAnnotation, knownPorts PortNameMap) (PortNameMap, error) {
portSet := make(PortNameMap)
var errList []error
for port, _ := range exposedNegPortMap {
// TODO: also validate ServicePorts in the exposed NEG annotation via webhook
if _, ok := knownPorts[port]; !ok {
errList = append(errList, fmt.Errorf("ServicePort %v doesn't exist on Service", port))
}
portSet[port] = knownPorts[port]
}

return portSet, utilerrors.NewAggregate(errList)
}

// NegServiceState contains name and zone of the Network Endpoint Group
// resources associated with this service
type NegServiceState struct {
// NetworkEndpointGroups returns the mapping between service port and NEG
// resource. key is service port, value is the name of the NEG resource.
NetworkEndpointGroups PortNameMap `json:"network_endpoint_groups,omitempty"`
Zones []string `json:"zones,omitempty"`
}

// GenNegServiceState generates a NegServiceState denoting the current NEGs
// associated with the given ports.
// NetworkEndpointGroups is a mapping between ServicePort and NEG name
// Zones is a list of zones where the NEGs exist.
func GenNegServiceState(zones []string, portToNegs PortNameMap) NegServiceState {
res := NegServiceState{}
res.NetworkEndpointGroups = make(PortNameMap)
res.Zones = zones
res.NetworkEndpointGroups = portToNegs
return res
}
Loading

0 comments on commit c89571a

Please sign in to comment.