istio/pkg/event/service_center_entry.go (127 lines of code) (raw):
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 event
import (
"fmt"
"net/url"
"strconv"
"strings"
"github.com/go-chassis/cari/discovery"
istioAPI "istio.io/api/networking/v1alpha3"
istioClient "istio.io/client-go/pkg/apis/networking/v1alpha3"
"istio.io/istio/pkg/config/protocol"
"istio.io/pkg/log"
k8s "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
// microservice protocol used for REST endpoints, equivalent to HTTP/HTTPS.
RestProtocol = "rest"
// Istio label corresponding to id of original microservice instance before being converted to WorkloadEntry.
InstanceIdLabel = "instanceId"
// microservice endpoint query string that enables SSL.
EnableSSL = "sslEnabled=true"
)
type ChangeEvent struct {
// Type of change event
Action discovery.EventType
// Event payload
Event
}
type InstanceEntry struct {
*discovery.MicroServiceInstance
}
// A Service center MicroService and its associated instance(s).
type MicroserviceEntry struct {
// Microservice struct
MicroService *discovery.MicroService
// Instances of the MicroService
Instances []*InstanceEntry
}
// Convert a MicroServiceInstance to an Istio WorkloadEntry event.
func (c *InstanceEntry) Convert() *WorkloadEntry {
// Istio ServiceEntry port names mapped to the "internal" port number of their target WorkloadEntry
portMap := map[string]uint32{}
// Istio ServiceEntry port names mapped to port structs
svcPortMap := map[string]*istioAPI.Port{}
var address string
if len(c.Endpoints) == 0 {
log.Errorf("service center Microservice Instance %v has no endpoint found\n", c.InstanceId)
return nil
}
for i, ep := range c.Endpoints {
u, err := url.Parse(ep)
if err != nil {
log.Errorf("service center Microservice Instance %v has endpoint \"%v\" that could not be parsed as a valid URL\n", c.InstanceId, ep)
return nil
}
if i == 0 {
// Only using first endpoint's hostname as the WorkloadEntry's address, Service center Microservice expects all endpoints for an instance to share a hostname
address = u.Hostname()
}
istioProtocol := getIstioProtocolFromURL(*u)
port, _ := strconv.Atoi(u.Port())
// Name for Istio ports are set to <protocol>-<portNumber>
portName := fmt.Sprintf("%s-%s", istioProtocol, u.Port())
svcPortMap[portName] = newIstioPort(portName, istioProtocol, uint32(port))
portMap[portName] = uint32(port)
}
// Convert ServiceEntry port map to array
svcPorts := []*istioAPI.Port{}
for _, port := range svcPortMap {
svcPorts = append(svcPorts, port)
}
return &WorkloadEntry{
WorkloadEntry: &istioAPI.WorkloadEntry{
Address: address,
Ports: portMap,
Labels: map[string]string{
"name": c.HostName,
InstanceIdLabel: c.InstanceId,
},
},
ServicePorts: svcPorts,
}
}
// Convert a MicroService to an Istio ServiceEntry.
func (c *MicroserviceEntry) Convert() *ServiceEntry {
ms := c.MicroService
insts := c.Instances
name := strings.ToLower(ms.ServiceName)
location := istioAPI.ServiceEntry_MESH_INTERNAL
resolution := istioAPI.ServiceEntry_STATIC
wles := []*istioAPI.WorkloadEntry{}
// Ensures that only one port struct exists for each port name
svcPortMap := map[string]*istioAPI.Port{}
for _, inst := range insts {
wle := inst.Convert()
if wle != nil {
workloadEntry := wle
for _, istioPort := range workloadEntry.ServicePorts {
svcPortMap[istioPort.Name] = istioPort
}
wles = append(wles, workloadEntry.WorkloadEntry)
}
}
// Convert port map to array representing all ports exposed by ServiceEntry
svcPorts := []*istioAPI.Port{}
for _, port := range svcPortMap {
svcPorts = append(svcPorts, port)
}
se := &istioClient.ServiceEntry{
ObjectMeta: k8s.ObjectMeta{
Name: name,
},
Spec: istioAPI.ServiceEntry{
Hosts: []string{name},
Ports: svcPorts,
Location: location,
Resolution: resolution,
Endpoints: wles,
},
}
return NewServiceEntry(se)
}
func getIstioProtocolFromURL(u url.URL) string {
p := u.Scheme
if p == RestProtocol {
// Microservice uses query string to signify HTTPS endpoint
if strings.Contains(u.RawQuery, EnableSSL) {
p = "https"
} else {
p = "http"
}
}
istioP := protocol.Parse(p)
if istioP.IsUnsupported() {
istioP = protocol.HTTP
}
return string(istioP)
}
// Construct an Istio ServiceEntry port.
func newIstioPort(name string, protocol string, number uint32) *istioAPI.Port {
return &istioAPI.Port{
Number: number,
TargetPort: number,
Protocol: protocol,
Name: name,
}
}