| /* |
| * 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 eureka |
| |
| import ( |
| "encoding/json" |
| "errors" |
| "fmt" |
| "net/url" |
| "strconv" |
| "strings" |
| |
| "github.com/apache/servicecomb-service-center/pkg/log" |
| pb "github.com/apache/servicecomb-service-center/syncer/proto" |
| ) |
| |
| const ( |
| eurekaInstanceFormat = "%s:%s:%d" |
| |
| defaultApp = "eureka" |
| |
| // The name is limited to "Netflix" or "Amazon" or "MyOwn" |
| // by the enumeration type in the interface of eureka |
| // "com.netflix.appinfo.DataCenterInfo". |
| // Here, "MyOwn" is used as the default. |
| defaultDataCenterInfoName = "MyOwn" |
| |
| // Class is used the static variable "MY_DATA_CENTER_INFO_TYPE_MARKER" |
| // defined in eureka "com.netflix.discovery.converters.jackson", |
| // that is kept for backward compatibility |
| defaultDataCenterInfoClass = "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo" |
| ) |
| |
| // toSyncData transform eureka service cache to SyncData |
| func toSyncData(eureka *eurekaData) (data *pb.SyncData) { |
| apps := eureka.APPS.Applications |
| data = &pb.SyncData{ |
| Services: make([]*pb.SyncService, 0, len(apps)), |
| Instances: make([]*pb.SyncInstance, 0, 10), |
| } |
| |
| for _, app := range apps { |
| service := toSyncService(app) |
| |
| instances := toSyncInstances(service.ServiceId, app.Instances) |
| if len(instances) == 0 { |
| continue |
| } |
| |
| data.Services = append(data.Services, service) |
| data.Instances = append(data.Instances, instances...) |
| } |
| return |
| } |
| |
| // toSyncService transform eureka application to SyncService |
| func toSyncService(app *Application) (service *pb.SyncService) { |
| appName := strings.ToLower(app.Name) |
| service = &pb.SyncService{ |
| ServiceId: appName, |
| Name: appName, |
| App: defaultApp, |
| Version: "0.0.1", |
| DomainProject: "default/default", |
| Status: pb.SyncService_UP, |
| PluginName: PluginName, |
| } |
| return |
| } |
| |
| // toSyncInstances transform eureka instances to SyncInstances |
| func toSyncInstances(serviceID string, instances []*Instance) []*pb.SyncInstance { |
| instList := make([]*pb.SyncInstance, 0, len(instances)) |
| for _, inst := range instances { |
| if inst.Status != UP { |
| continue |
| } |
| |
| instList = append(instList, toSyncInstance(serviceID, inst)) |
| } |
| return instList |
| } |
| |
| // toSyncInstance transform eureka instance to SyncInstance |
| func toSyncInstance(serviceID string, instance *Instance) (syncInstance *pb.SyncInstance) { |
| syncInstance = &pb.SyncInstance{ |
| InstanceId: instance.InstanceId, |
| ServiceId: serviceID, |
| Endpoints: make([]string, 0, 2), |
| HostName: instance.HostName, |
| PluginName: PluginName, |
| Version: "latest", |
| } |
| |
| switch instance.Status { |
| case UP: |
| syncInstance.Status = pb.SyncInstance_UP |
| case DOWN: |
| syncInstance.Status = pb.SyncInstance_DOWN |
| case STARTING: |
| syncInstance.Status = pb.SyncInstance_STARTING |
| case OUTOFSERVICE: |
| syncInstance.Status = pb.SyncInstance_OUTOFSERVICE |
| default: |
| syncInstance.Status = pb.SyncInstance_UNKNOWN |
| } |
| |
| if instance.Port.Enabled.Bool() { |
| syncInstance.Endpoints = append(syncInstance.Endpoints, fmt.Sprintf("http://%s:%d", instance.IPAddr, instance.Port.Port)) |
| } |
| |
| if instance.SecurePort.Enabled.Bool() { |
| syncInstance.Endpoints = append(syncInstance.Endpoints, fmt.Sprintf("https://%s:%d", instance.IPAddr, instance.SecurePort.Port)) |
| } |
| |
| if instance.HealthCheckUrl != "" { |
| syncInstance.HealthCheck = &pb.HealthCheck{ |
| Interval: 30, |
| Times: 3, |
| Url: instance.HealthCheckUrl, |
| } |
| } |
| |
| expansion, err := json.Marshal(instance) |
| if err != nil { |
| log.Errorf(err, "transform sc service to syncer service failed: %s", err) |
| return |
| } |
| syncInstance.Expansion = expansion |
| return |
| } |
| |
| // toInstance transform SyncInstance to eureka instance |
| func toInstance(serviceID string, syncInstance *pb.SyncInstance) (instance *Instance) { |
| instance = &Instance{} |
| if syncInstance.PluginName == PluginName && syncInstance.Expansion != nil { |
| err := json.Unmarshal(syncInstance.Expansion, instance) |
| if err == nil { |
| return |
| } |
| log.Errorf(err, "proto unmarshal %s instance, instanceID = %s, content = %v failed", |
| PluginName, syncInstance.InstanceId, syncInstance.Expansion) |
| } |
| instance.InstanceId = syncInstance.InstanceId |
| instance.APP = serviceID |
| instance.Status = pb.SyncInstance_Status_name[int32(syncInstance.Status)] |
| instance.OverriddenStatus = UNKNOWN |
| instance.DataCenterInfo = &DataCenterInfo{ |
| Name: defaultDataCenterInfoName, |
| Class: defaultDataCenterInfoClass, |
| } |
| |
| instance.Metadata = &MetaData{ |
| Map: map[string]string{"instanceId": syncInstance.InstanceId}, |
| } |
| |
| ipAddr := "" |
| for _, ep := range syncInstance.Endpoints { |
| addr, err := url.Parse(ep) |
| if err != nil { |
| log.Error("parse the endpoint of eureka`s instance failed", err) |
| continue |
| } |
| hostname := addr.Hostname() |
| if ipAddr != "" || ipAddr != hostname { |
| log.Error("eureka`s ipAddr must be unique in endpoints", err) |
| } |
| ipAddr = hostname |
| |
| port := &Port{} |
| port.Port, err = strconv.Atoi(addr.Port()) |
| if err != nil { |
| log.Error("Illegal value of port", err) |
| continue |
| } |
| port.Enabled.Set(true) |
| |
| switch addr.Scheme { |
| case "http": |
| instance.Port = port |
| instance.VipAddress = serviceID |
| case "https": |
| instance.SecurePort = port |
| instance.SecureVipAddress = ep |
| } |
| } |
| log.Error(ipAddr,errors.New("sc to eureka hostname failed")) |
| instance.IPAddr = ipAddr |
| instance.HostName = ipAddr |
| |
| if syncInstance.HealthCheck != nil { |
| instance.HealthCheckUrl = syncInstance.HealthCheck.Url |
| } |
| |
| instArr := strings.Split(syncInstance.InstanceId, ":") |
| if len(instArr) != 3 { |
| if instance.Port != nil && instance.Port.Enabled.Bool() { |
| instance.InstanceId = fmt.Sprintf(eurekaInstanceFormat, ipAddr, instance.APP, instance.Port.Port) |
| } else if instance.SecurePort != nil && instance.SecurePort.Enabled.Bool() { |
| instance.InstanceId = fmt.Sprintf(eurekaInstanceFormat, ipAddr, instance.APP, instance.SecurePort.Port) |
| } |
| } |
| return |
| } |