From 5b573b66ea5e15f4ff2ee2d2ac735126f5ad04f3 Mon Sep 17 00:00:00 2001 From: tb365 Date: Wed, 27 Oct 2021 09:14:29 +0800 Subject: [PATCH] support gcp regional lb sync --- pkg/apis/compute/loadbalancer_const.go | 1 + pkg/multicloud/google/google.go | 14 +- pkg/multicloud/google/loadbalancer.go | 422 ++++++++++ pkg/multicloud/google/loadbalancer_backend.go | 186 +++++ .../google/loadbalancer_backendgroup.go | 154 ++++ pkg/multicloud/google/loadbalancer_cert.go | 154 ++++ .../google/loadbalancer_components.go | 727 ++++++++++++++++++ .../google/loadbalancer_listener.go | 573 ++++++++++++++ .../google/loadbalancer_listenerrule.go | 163 ++++ pkg/multicloud/google/region.go | 6 +- pkg/multicloud/google/shell/loadbalancer.go | 214 ++++++ 11 files changed, 2610 insertions(+), 4 deletions(-) create mode 100644 pkg/multicloud/google/loadbalancer.go create mode 100644 pkg/multicloud/google/loadbalancer_backend.go create mode 100644 pkg/multicloud/google/loadbalancer_backendgroup.go create mode 100644 pkg/multicloud/google/loadbalancer_cert.go create mode 100644 pkg/multicloud/google/loadbalancer_components.go create mode 100644 pkg/multicloud/google/loadbalancer_listener.go create mode 100644 pkg/multicloud/google/loadbalancer_listenerrule.go create mode 100644 pkg/multicloud/google/shell/loadbalancer.go diff --git a/pkg/apis/compute/loadbalancer_const.go b/pkg/apis/compute/loadbalancer_const.go index fd16df1803..752dd3907f 100644 --- a/pkg/apis/compute/loadbalancer_const.go +++ b/pkg/apis/compute/loadbalancer_const.go @@ -332,6 +332,7 @@ const ( LB_SCHEDULER_SCH = "sch" // source-ip-based consistent hash LB_SCHEDULER_TCH = "tch" // 4-tuple-based consistent hash LB_SCHEDULER_QCH = "qch" + LB_SCHEDULER_MH = "mh" // maglev consistent hash ) var LB_SCHEDULER_TYPES = choices.NewChoices( diff --git a/pkg/multicloud/google/google.go b/pkg/multicloud/google/google.go index f3f5891b53..65d23c00d2 100644 --- a/pkg/multicloud/google/google.go +++ b/pkg/multicloud/google/google.go @@ -248,8 +248,12 @@ func (self *SGoogleClient) ecsGet(resource string, retval interface{}) error { } func (self *SGoogleClient) ecsList(resource string, params map[string]string) (jsonutils.JSONObject, error) { + return self._ecsList("GET", resource, params) +} + +func (self *SGoogleClient) _ecsList(method, resource string, params map[string]string) (jsonutils.JSONObject, error) { resource = fmt.Sprintf("projects/%s/%s", self.projectId, resource) - return jsonRequest(self.client, "GET", GOOGLE_COMPUTE_DOMAIN, GOOGLE_API_VERSION, resource, params, nil, self.debug) + return jsonRequest(self.client, httputils.THttpMethod(method), GOOGLE_COMPUTE_DOMAIN, GOOGLE_API_VERSION, resource, params, nil, self.debug) } func (self *SGoogleClient) managerList(resource string, params map[string]string) (jsonutils.JSONObject, error) { @@ -320,6 +324,10 @@ func (self *SGoogleClient) iamListAll(resource string, params map[string]string, } func (self *SGoogleClient) ecsListAll(resource string, params map[string]string, retval interface{}) error { + return self._ecsListAll("GET", resource, params, retval) +} + +func (self *SGoogleClient) _ecsListAll(method string, resource string, params map[string]string, retval interface{}) error { if params == nil { params = map[string]string{} } @@ -328,7 +336,7 @@ func (self *SGoogleClient) ecsListAll(resource string, params map[string]string, params["maxResults"] = "500" for { params["pageToken"] = nextPageToken - resp, err := self.ecsList(resource, params) + resp, err := self._ecsList(method, resource, params) if err != nil { return errors.Wrap(err, "ecsList") } @@ -926,7 +934,7 @@ func (self *SGoogleClient) GetCapabilities() []string { cloudprovider.CLOUD_CAPABILITY_PROJECT, cloudprovider.CLOUD_CAPABILITY_COMPUTE, cloudprovider.CLOUD_CAPABILITY_NETWORK, - // cloudprovider.CLOUD_CAPABILITY_LOADBALANCER, + cloudprovider.CLOUD_CAPABILITY_LOADBALANCER, cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE, cloudprovider.CLOUD_CAPABILITY_RDS, // cloudprovider.CLOUD_CAPABILITY_CACHE, diff --git a/pkg/multicloud/google/loadbalancer.go b/pkg/multicloud/google/loadbalancer.go new file mode 100644 index 0000000000..c2e1e461e7 --- /dev/null +++ b/pkg/multicloud/google/loadbalancer.go @@ -0,0 +1,422 @@ +package google + +import ( + "context" + "fmt" + "strings" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/utils" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +// 全球负载均衡 https://cloud.google.com/compute/docs/reference/rest/v1/globalAddresses/list +// 区域负载均衡 https://cloud.google.com/compute/docs/reference/rest/v1/addresses +// https://cloud.google.com/compute/docs/reference/rest/v1/targetHttpProxies/get +// https://cloud.google.com/compute/docs/reference/rest/v1/targetGrpcProxies/get +// https://cloud.google.com/compute/docs/reference/rest/v1/targetHttpsProxies/get +// https://cloud.google.com/compute/docs/reference/rest/v1/targetSslProxies/get +// https://cloud.google.com/compute/docs/reference/rest/v1/targetTcpProxies/get + +type SLoadbalancer struct { + SResourceBase + region *SRegion + urlMap *SUrlMap // http & https LB + backendServices []SBackendServices // tcp & udp LB. 或者 http & https 后端 + instanceGroups []SInstanceGroup + healthChecks []HealthChecks + + forwardRules []SForwardingRule // 服务IP地址 + isHttpLb bool // 标记是否为http/https lb +} + +func (self *SLoadbalancer) GetStatus() string { + return api.LB_STATUS_ENABLED +} + +func (self *SLoadbalancer) Refresh() error { + lb, err := self.region.GetLoadbalancer(self.GetGlobalId()) + if err != nil { + return errors.Wrap(err, "GetLoadbalancer") + } + + err = jsonutils.Update(self, &lb) + if err != nil { + return errors.Wrap(err, "Refresh.Update") + } + + self.healthChecks = nil + self.instanceGroups = nil + self.forwardRules = nil + if self.isHttpLb { + bss, err := self.GetBackendServices() + if err != nil { + return errors.Wrap(err, "GetForwardingRules") + } + self.backendServices = bss + } + return nil +} + +func (self *SLoadbalancer) IsEmulated() bool { + return true +} + +func (self *SLoadbalancer) GetSysTags() map[string]string { + return nil +} + +func (self *SLoadbalancer) GetTags() (map[string]string, error) { + return map[string]string{}, nil +} + +func (self *SLoadbalancer) SetTags(tags map[string]string, replace bool) error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadbalancer) GetProjectId() string { + return self.region.GetProjectId() +} + +/* +对应forwardingRules地址,存在多个前端IP的情况下,只展示按拉丁字母排序最前的一个地址。其他地址需要到详情中查看 +*/ +func (self *SLoadbalancer) GetAddress() string { + frs, err := self.GetForwardingRules() + if err != nil { + log.Errorf("GetAddress.GetForwardingRules %s", err) + } + + for i := range frs { + if frs[i].LoadBalancingScheme != "EXTERNAL" { + return frs[i].IPAddress + } + } + + return "" +} + +func (self *SLoadbalancer) GetAddressType() string { + eip, err := self.GetIEIP() + if err != nil { + return api.LB_ADDR_TYPE_INTRANET + } + if eip == nil { + return api.LB_ADDR_TYPE_INTRANET + } + return api.LB_ADDR_TYPE_INTERNET +} + +func (self *SLoadbalancer) GetNetworkType() string { + return api.LB_NETWORK_TYPE_VPC +} + +func (self *SLoadbalancer) GetNetworkIds() []string { + igs, err := self.GetInstanceGroups() + if err != nil { + log.Errorf("GetInstanceGroups %s", err) + return nil + } + + selfLinks := make([]string, 0) + networkIds := make([]string, 0) + for i := range igs { + if utils.IsInStringArray(igs[i].Network, selfLinks) { + selfLinks = append(selfLinks, igs[i].Network) + network := SResourceBase{ + Name: "", + SelfLink: igs[i].Network, + } + networkIds = append(networkIds, network.GetGlobalId()) + } + } + + return networkIds +} + +func (self *SLoadbalancer) GetVpcId() string { + return "" +} + +func (self *SLoadbalancer) GetZoneId() string { + igs, err := self.GetInstanceGroups() + if err != nil { + log.Errorf("GetInstanceGroups %s", err) + return "" + } + + for i := range igs { + if len(igs[i].Zone) > 0 { + zone := SResourceBase{ + Name: "", + SelfLink: igs[i].Zone, + } + + return zone.GetGlobalId() + } + } + + return "" +} + +func (self *SLoadbalancer) GetZone1Id() string { + return "" +} + +func (self *SLoadbalancer) GetLoadbalancerSpec() string { + if self.isHttpLb { + return "regional_http_lb" + } + + return fmt.Sprintf("regional_%s", strings.ToLower(self.backendServices[0].Protocol)) +} + +func (self *SLoadbalancer) GetChargeType() string { + return api.LB_CHARGE_TYPE_POSTPAID +} + +func (self *SLoadbalancer) GetEgressMbps() int { + return 0 +} + +func (self *SLoadbalancer) GetIEIP() (cloudprovider.ICloudEIP, error) { + frs, err := self.GetForwardingRules() + if err != nil { + log.Errorf("GetAddress.GetForwardingRules %s", err) + } + + for i := range frs { + if frs[i].LoadBalancingScheme == "EXTERNAL" { + eips, err := self.region.GetEips(frs[i].IPAddress, 0, "") + if err != nil { + log.Errorf("GetEips %s", err) + } + + if len(eips) > 0 { + return &eips[0], nil + } + } + } + + return nil, nil +} + +func (self *SLoadbalancer) Delete(ctx context.Context) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SLoadbalancer) Start() error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadbalancer) Stop() error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadbalancer) GetILoadBalancerListeners() ([]cloudprovider.ICloudLoadbalancerListener, error) { + lbls, err := self.GetLoadbalancerListeners() + if err != nil { + return nil, errors.Wrap(err, "GetLoadbalancerListeners") + } + + ilbls := make([]cloudprovider.ICloudLoadbalancerListener, len(lbls)) + for i := range lbls { + ilbls[i] = &lbls[i] + } + + return ilbls, nil +} + +func (self *SLoadbalancer) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) { + lbbgs, err := self.GetLoadbalancerBackendGroups() + if err != nil { + return nil, errors.Wrap(err, "GetLoadbalancerBackendGroups") + } + + ilbbgs := make([]cloudprovider.ICloudLoadbalancerBackendGroup, len(lbbgs)) + for i := range lbbgs { + ilbbgs[i] = &lbbgs[i] + } + + return ilbbgs, nil +} + +func (self *SLoadbalancer) CreateILoadBalancerBackendGroup(group *cloudprovider.SLoadbalancerBackendGroup) (cloudprovider.ICloudLoadbalancerBackendGroup, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SLoadbalancer) GetILoadBalancerBackendGroupById(groupId string) (cloudprovider.ICloudLoadbalancerBackendGroup, error) { + lbbgs, err := self.GetLoadbalancerBackendGroups() + if err != nil { + return nil, errors.Wrap(err, "GetLoadbalancerBackendGroups") + } + + for i := range lbbgs { + if lbbgs[i].GetGlobalId() == groupId { + return &lbbgs[i], nil + } + } + + return nil, cloudprovider.ErrNotFound +} + +func (self *SLoadbalancer) CreateILoadBalancerListener(ctx context.Context, listener *cloudprovider.SLoadbalancerListener) (cloudprovider.ICloudLoadbalancerListener, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SLoadbalancer) GetILoadBalancerListenerById(listenerId string) (cloudprovider.ICloudLoadbalancerListener, error) { + lbls, err := self.GetLoadbalancerListeners() + if err != nil { + return nil, errors.Wrap(err, "GetLoadbalancerBackendGroups") + } + + for i := range lbls { + if lbls[i].GetGlobalId() == listenerId { + return &lbls[i], nil + } + } + + return nil, cloudprovider.ErrNotFound +} + +// GET https://compute.googleapis.com/compute/v1/projects/{project}/aggregated/targetHttpProxies 前端监听 +// tcp lb backend type: backend service +func (self *SRegion) GetRegionalTcpLoadbalancers() ([]SLoadbalancer, error) { + bss, err := self.GetRegionalBackendServices("protocol eq TCP") + if err != nil { + return nil, errors.Wrap(err, "GetRegionalBackendServices") + } + + lbs := make([]SLoadbalancer, len(bss)) + for i := range bss { + lbs[i] = SLoadbalancer{ + region: self, + SResourceBase: SResourceBase{ + Name: bss[i].Name, + SelfLink: bss[i].SelfLink, + }, + urlMap: nil, + backendServices: []SBackendServices{bss[i]}, + forwardRules: nil, + } + } + + return lbs, nil +} + +// udp lb backend type: backend service +func (self *SRegion) GetRegionalUdpLoadbalancers() ([]SLoadbalancer, error) { + bss, err := self.GetRegionalBackendServices("protocol eq UDP") + if err != nil { + return nil, errors.Wrap(err, "GetRegionalBackendServices") + } + + lbs := make([]SLoadbalancer, len(bss)) + for i := range bss { + lbs[i] = SLoadbalancer{ + region: self, + SResourceBase: SResourceBase{ + Name: bss[i].Name, + SelfLink: bss[i].SelfLink, + }, + urlMap: nil, + backendServices: []SBackendServices{bss[i]}, + forwardRules: nil, + } + } + + return lbs, nil +} + +// http&https lb: urlmaps +func (self *SRegion) GetRegionalHTTPLoadbalancers() ([]SLoadbalancer, error) { + ums, err := self.GetRegionalUrlMaps("") + if err != nil { + return nil, errors.Wrap(err, "GetRegionalUrlMaps") + } + + lbs := make([]SLoadbalancer, len(ums)) + for i := range ums { + lbs[i] = SLoadbalancer{ + region: self, + SResourceBase: SResourceBase{ + Name: ums[i].Name, + SelfLink: ums[i].SelfLink, + }, + urlMap: &ums[i], + backendServices: nil, + forwardRules: nil, + isHttpLb: true, + } + } + + return lbs, nil +} + +func (self *SRegion) GetRegionalLoadbalancers() ([]SLoadbalancer, error) { + lbs := make([]SLoadbalancer, 0) + funcs := []func() ([]SLoadbalancer, error){self.GetRegionalHTTPLoadbalancers, self.GetRegionalTcpLoadbalancers, self.GetRegionalUdpLoadbalancers} + for i := range funcs { + _lbs, err := funcs[i]() + if err != nil { + return nil, errors.Wrap(err, "GetRegionalLoadbalancers") + } + lbs = append(lbs, _lbs...) + } + + return lbs, nil +} + +func (self *SRegion) GetLoadbalancer(resourceId string) (SLoadbalancer, error) { + lb := SLoadbalancer{} + var err error + if strings.Contains(resourceId, "/urlMaps/") { + ret := SUrlMap{} + err = self.Get(resourceId, &ret) + lb.isHttpLb = true + lb.urlMap = &ret + lb.SResourceBase = SResourceBase{ + Name: ret.Name, + SelfLink: ret.SelfLink, + } + } else { + ret := SBackendServices{} + err = self.Get(resourceId, &ret) + lb.backendServices = []SBackendServices{ret} + lb.SResourceBase = SResourceBase{ + Name: ret.Name, + SelfLink: ret.SelfLink, + } + } + + if err != nil { + return lb, errors.Wrapf(err, "get") + } + + lb.region = self + return lb, nil +} + +func (self *SRegion) GetILoadBalancers() ([]cloudprovider.ICloudLoadbalancer, error) { + lbs, err := self.GetRegionalLoadbalancers() + if err != nil { + return nil, errors.Wrap(err, "GetRegionalLoadbalancers") + } + ilbs := []cloudprovider.ICloudLoadbalancer{} + for i := range lbs { + ilbs = append(ilbs, &lbs[i]) + } + return ilbs, nil +} + +func (self *SRegion) GetILoadBalancerById(loadbalancerId string) (cloudprovider.ICloudLoadbalancer, error) { + lb, err := self.GetLoadbalancer(loadbalancerId) + if err != nil { + return nil, errors.Wrap(err, "GetLoadbalancer") + } + return &lb, nil +} diff --git a/pkg/multicloud/google/loadbalancer_backend.go b/pkg/multicloud/google/loadbalancer_backend.go new file mode 100644 index 0000000000..a43bd2a8f1 --- /dev/null +++ b/pkg/multicloud/google/loadbalancer_backend.go @@ -0,0 +1,186 @@ +package google + +import ( + "context" + "fmt" + "strconv" + "strings" + + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SLoadbalancerBackend struct { + lbbg *SLoadBalancerBackendGroup + + backendService SBackendServices // + instanceGroup SInstanceGroup // 实例组 + Backend SInstanceGroupInstance // backend + + Port int `json:"port"` +} + +func (self *SLoadbalancerBackend) GetId() string { + return fmt.Sprintf("%s::%s::%s::%d", self.lbbg.GetGlobalId(), self.instanceGroup.GetGlobalId(), self.GetBackendId(), self.Port) +} + +func (self *SLoadbalancerBackend) GetName() string { + segs := strings.Split(self.Backend.Instance, "/") + name := "" + if len(segs) > 0 { + name = segs[len(segs)-1] + } + return fmt.Sprintf("%s::%s::%d", self.instanceGroup.GetName(), name, self.Port) +} + +func (self *SLoadbalancerBackend) GetGlobalId() string { + return self.GetId() +} + +func (self *SLoadbalancerBackend) GetStatus() string { + return api.LB_STATUS_ENABLED +} + +func (self *SLoadbalancerBackend) Refresh() error { + return nil +} + +func (self *SLoadbalancerBackend) IsEmulated() bool { + return true +} + +func (self *SLoadbalancerBackend) GetSysTags() map[string]string { + return nil +} + +func (self *SLoadbalancerBackend) GetTags() (map[string]string, error) { + return nil, nil +} + +func (self *SLoadbalancerBackend) SetTags(tags map[string]string, replace bool) error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadbalancerBackend) GetProjectId() string { + return self.lbbg.GetProjectId() +} + +func (self *SLoadbalancerBackend) GetWeight() int { + return 0 +} + +func (self *SLoadbalancerBackend) GetPort() int { + return self.Port +} + +func (self *SLoadbalancerBackend) GetBackendType() string { + return api.LB_BACKEND_GUEST +} + +func (self *SLoadbalancerBackend) GetBackendRole() string { + return api.LB_BACKEND_ROLE_DEFAULT +} + +func (self *SLoadbalancerBackend) GetBackendId() string { + r := SResourceBase{ + Name: "", + SelfLink: self.Backend.Instance, + } + return r.GetGlobalId() +} + +func (self *SLoadbalancerBackend) GetIpAddress() string { + return "" +} + +func (self *SLoadbalancerBackend) SyncConf(ctx context.Context, port, weight int) error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadBalancerBackendGroup) GetLoadbalancerBackends() ([]SLoadbalancerBackend, error) { + if self.backends != nil { + return self.backends, nil + } + + _igs, err := self.lb.GetInstanceGroupsMap() + if err != nil { + return nil, errors.Wrap(err, "GetInstanceGroupsMap") + } + + igs := make([]SInstanceGroup, 0) + for i := range self.backendService.Backends { + backend := self.backendService.Backends[i] + if v, ok := _igs[backend.Group]; ok { + igs = append(igs, v) + } + } + + ret := make([]SLoadbalancerBackend, 0) + for i := range igs { + ig := igs[i] + // http lb + if self.lb.isHttpLb { + for j := range ig.NamedPorts { + np := ig.NamedPorts[j] + if np.Name != self.backendService.PortName { + continue + } + + bs, err := ig.GetInstances() + if err != nil { + return nil, errors.Wrap(err, "GetInstances") + } + + for n := range bs { + backend := SLoadbalancerBackend{ + lbbg: self, + backendService: self.backendService, + instanceGroup: ig, + Backend: bs[n], + Port: int(np.Port), + } + + ret = append(ret, backend) + } + } + } else { + // tcp & udp lb + bs, err := ig.GetInstances() + if err != nil { + return nil, errors.Wrap(err, "GetInstances") + } + + frs, err := self.lb.GetForwardingRules() + if err != nil { + return nil, errors.Wrap(err, "GetForwardingRules") + } + + for m := range frs { + fr := frs[m] + for p := range fr.Ports { + port, _ := strconv.Atoi(fr.Ports[p]) + if port <= 0 { + continue + } + + for n := range bs { + backend := SLoadbalancerBackend{ + lbbg: self, + backendService: self.backendService, + instanceGroup: ig, + Backend: bs[n], + Port: port, + } + + ret = append(ret, backend) + } + } + } + } + } + + self.backends = ret + return ret, nil +} diff --git a/pkg/multicloud/google/loadbalancer_backendgroup.go b/pkg/multicloud/google/loadbalancer_backendgroup.go new file mode 100644 index 0000000000..c4015ae5be --- /dev/null +++ b/pkg/multicloud/google/loadbalancer_backendgroup.go @@ -0,0 +1,154 @@ +package google + +import ( + "context" + + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SLoadBalancerBackendGroup struct { + lb *SLoadbalancer + backends []SLoadbalancerBackend + + backendService SBackendServices // + + Id string `json:"id"` + Name string `json:"name"` +} + +func (self *SLoadBalancerBackendGroup) GetId() string { + return self.Id +} + +func (self *SLoadBalancerBackendGroup) GetName() string { + return self.Name +} + +func (self *SLoadBalancerBackendGroup) GetGlobalId() string { + return self.backendService.GetGlobalId() +} + +func (self *SLoadBalancerBackendGroup) GetStatus() string { + return api.LB_STATUS_ENABLED +} + +func (self *SLoadBalancerBackendGroup) Refresh() error { + return nil +} + +func (self *SLoadBalancerBackendGroup) IsEmulated() bool { + return true +} + +func (self *SLoadBalancerBackendGroup) GetSysTags() map[string]string { + return nil +} + +func (self *SLoadBalancerBackendGroup) GetTags() (map[string]string, error) { + return nil, nil +} + +func (self *SLoadBalancerBackendGroup) SetTags(tags map[string]string, replace bool) error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadBalancerBackendGroup) GetProjectId() string { + return self.lb.GetProjectId() +} + +func (self *SLoadBalancerBackendGroup) IsDefault() bool { + return false +} + +func (self *SLoadBalancerBackendGroup) GetType() string { + return api.LB_BACKENDGROUP_TYPE_NORMAL +} + +func (self *SLoadBalancerBackendGroup) GetLoadbalancerId() string { + return self.lb.GetGlobalId() +} + +func (self *SLoadBalancerBackendGroup) GetILoadbalancerBackends() ([]cloudprovider.ICloudLoadbalancerBackend, error) { + backends, err := self.GetLoadbalancerBackends() + if err != nil { + return nil, errors.Wrap(err, "GetLoadbalancerBackends") + } + + ibackends := make([]cloudprovider.ICloudLoadbalancerBackend, len(backends)) + for i := range backends { + ibackends[i] = &backends[i] + } + + return ibackends, nil +} + +func (self *SLoadBalancerBackendGroup) GetILoadbalancerBackendById(backendId string) (cloudprovider.ICloudLoadbalancerBackend, error) { + backends, err := self.GetLoadbalancerBackends() + if err != nil { + return nil, errors.Wrap(err, "GetLoadbalancerBackends") + } + + for i := range backends { + if backends[i].GetGlobalId() == backendId { + return &backends[i], nil + } + } + + return nil, cloudprovider.ErrNotFound +} + +func (self *SLoadBalancerBackendGroup) GetProtocolType() string { + return "" +} + +func (self *SLoadBalancerBackendGroup) GetScheduler() string { + return "" +} + +func (self *SLoadBalancerBackendGroup) GetHealthCheck() (*cloudprovider.SLoadbalancerHealthCheck, error) { + return nil, nil +} + +func (self *SLoadBalancerBackendGroup) GetStickySession() (*cloudprovider.SLoadbalancerStickySession, error) { + return nil, nil +} + +func (self *SLoadBalancerBackendGroup) AddBackendServer(serverId string, weight int, port int) (cloudprovider.ICloudLoadbalancerBackend, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SLoadBalancerBackendGroup) RemoveBackendServer(serverId string, weight int, port int) error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadBalancerBackendGroup) Delete(ctx context.Context) error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadBalancerBackendGroup) Sync(ctx context.Context, group *cloudprovider.SLoadbalancerBackendGroup) error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadbalancer) GetLoadbalancerBackendGroups() ([]SLoadBalancerBackendGroup, error) { + bss, err := self.GetBackendServices() + if err != nil { + return nil, errors.Wrap(err, "GetBackendServices") + } + + ret := make([]SLoadBalancerBackendGroup, 0) + for i := range bss { + group := SLoadBalancerBackendGroup{ + lb: self, + backendService: bss[i], + Id: bss[i].GetId(), + Name: bss[i].GetName(), + } + + ret = append(ret, group) + } + + return ret, nil +} diff --git a/pkg/multicloud/google/loadbalancer_cert.go b/pkg/multicloud/google/loadbalancer_cert.go new file mode 100644 index 0000000000..ead84f88d3 --- /dev/null +++ b/pkg/multicloud/google/loadbalancer_cert.go @@ -0,0 +1,154 @@ +package google + +import ( + "crypto/sha256" + "crypto/x509" + "encoding/hex" + "encoding/pem" + "strings" + "time" + + "yunion.io/x/log" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SLoadbalancerCertificate struct { + region *SRegion + SResourceBase + cert *x509.Certificate + + ID string `json:"id"` + CreationTimestamp string `json:"creationTimestamp"` + Certificate string `json:"certificate"` + SelfManaged SelfManaged `json:"selfManaged"` + Type string `json:"type"` + ExpireTime time.Time `json:"expireTime"` + Region string `json:"region"` + Kind string `json:"kind"` +} + +type SelfManaged struct { + Certificate string `json:"certificate"` +} + +func (self *SLoadbalancerCertificate) GetStatus() string { + return api.LB_STATUS_ENABLED +} + +func (self *SLoadbalancerCertificate) Refresh() error { + return nil +} + +func (self *SLoadbalancerCertificate) IsEmulated() bool { + return false +} + +func (self *SLoadbalancerCertificate) GetSysTags() map[string]string { + return nil +} + +func (self *SLoadbalancerCertificate) GetTags() (map[string]string, error) { + return nil, nil +} + +func (self *SLoadbalancerCertificate) SetTags(tags map[string]string, replace bool) error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadbalancerCertificate) GetProjectId() string { + return self.region.GetProjectId() +} + +func (self *SLoadbalancerCertificate) Sync(name, privateKey, publickKey string) error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadbalancerCertificate) Delete() error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadbalancerCertificate) GetCommonName() string { + c := self.getCert() + if c == nil { + return "" + } + return c.Subject.CommonName +} + +func (self *SLoadbalancerCertificate) GetSubjectAlternativeNames() string { + c := self.getCert() + if c == nil { + return "" + } + + names := []string{} + for i := range c.Extensions { + names = append(names, string(c.Extensions[i].Value)) + } + + return strings.Join(names, ",") +} + +func (self *SLoadbalancerCertificate) getCert() *x509.Certificate { + if self.cert != nil { + return self.cert + } + + p, _ := pem.Decode([]byte(self.Certificate)) + c, err := x509.ParseCertificate(p.Bytes) + if err != nil { + log.Errorf("get certificate %s(%s): %s", self.Name, self.GetId(), err) + return nil + } + + self.cert = c + return c +} + +func (self *SLoadbalancerCertificate) GetFingerprint() string { + c := self.getCert() + if c == nil { + return "" + } + d := sha256.Sum256(c.Raw) + return api.LB_TLS_CERT_FINGERPRINT_ALGO_SHA256 + ":" + hex.EncodeToString(d[:]) +} + +func (self *SLoadbalancerCertificate) GetExpireTime() time.Time { + return self.ExpireTime +} + +func (self *SLoadbalancerCertificate) GetPublickKey() string { + return "" +} + +func (self *SLoadbalancerCertificate) GetPrivateKey() string { + return "" +} + +func (self *SRegion) GetILoadBalancerCertificates() ([]cloudprovider.ICloudLoadbalancerCertificate, error) { + certs, err := self.GetRegionalSslCertificates("") + if err != nil { + return nil, errors.Wrap(err, "GetRegionalSslCertificates") + } + + icerts := make([]cloudprovider.ICloudLoadbalancerCertificate, len(certs)) + for i := range certs { + icerts[i] = &certs[i] + } + + return icerts, nil +} + +func (self *SRegion) GetILoadBalancerCertificateById(certId string) (cloudprovider.ICloudLoadbalancerCertificate, error) { + ret := SLoadbalancerCertificate{} + err := self.Get(certId, &ret) + if err != nil { + return nil, errors.Wrap(err, "Get") + } + ret.region = self + return &ret, nil +} diff --git a/pkg/multicloud/google/loadbalancer_components.go b/pkg/multicloud/google/loadbalancer_components.go new file mode 100644 index 0000000000..b66cd957d2 --- /dev/null +++ b/pkg/multicloud/google/loadbalancer_components.go @@ -0,0 +1,727 @@ +package google + +import ( + "fmt" + "strings" + + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/utils" + + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SUrlMap struct { + SResourceBase + + ID string `json:"id"` + CreationTimestamp string `json:"creationTimestamp"` + HostRules []HostRule `json:"hostRules"` + PathMatchers []PathMatcher `json:"pathMatchers"` + DefaultService string `json:"defaultService"` + Fingerprint string `json:"fingerprint"` + Region string `json:"region"` + Kind string `json:"kind"` +} + +type SForwardingRule struct { + SResourceBase + + ID string `json:"id"` + CreationTimestamp string `json:"creationTimestamp"` + Description string `json:"description"` + Region string `json:"region"` + IPAddress string `json:"IPAddress"` + IPProtocol string `json:"IPProtocol"` + PortRange string `json:"portRange"` + Target string `json:"target"` + LoadBalancingScheme string `json:"loadBalancingScheme"` + Subnetwork string `json:"subnetwork"` + Network string `json:"network"` + NetworkTier string `json:"networkTier"` + LabelFingerprint string `json:"labelFingerprint"` + Fingerprint string `json:"fingerprint"` + Kind string `json:"kind"` + Ports []string `json:"ports"` + BackendService string `json:"backendService"` +} + +// +//type STargetProxy struct { +//} +// +type SBackendServices struct { + SResourceBase + + ID string `json:"id"` + CreationTimestamp string `json:"creationTimestamp"` + Description string `json:"description"` + Backends []Backend `json:"backends"` + HealthChecks []string `json:"healthChecks"` + TimeoutSEC int64 `json:"timeoutSec"` + Port int64 `json:"port"` + Protocol string `json:"protocol"` + Fingerprint string `json:"fingerprint"` + PortName string `json:"portName"` + SessionAffinity string `json:"sessionAffinity"` + AffinityCookieTTLSEC int64 `json:"affinityCookieTtlSec"` + Region string `json:"region"` + LoadBalancingScheme string `json:"loadBalancingScheme"` + ConnectionDraining ConnectionDraining `json:"connectionDraining"` + LocalityLBPolicy string `json:"localityLbPolicy"` + ConsistentHash ConsistentHash `json:"consistentHash"` + Kind string `json:"kind"` + EnableCDN bool `json:"enableCDN"` + Network string `json:"network"` +} + +// +//type STargetPool struct { +//} + +type STargetHttpProxy struct { + SResourceBase + + ID string `json:"id"` + CreationTimestamp string `json:"creationTimestamp"` + Description string `json:"description"` + URLMap string `json:"urlMap"` + Region string `json:"region"` + ProxyBind bool `json:"proxyBind"` + Fingerprint string `json:"fingerprint"` + Kind string `json:"kind"` +} + +type STargetHttpsProxy struct { + SResourceBase + + ID string `json:"id"` + CreationTimestamp string `json:"creationTimestamp"` + Description string `json:"description"` + URLMap string `json:"urlMap"` + SSLCertificates []string `json:"sslCertificates"` + QuicOverride string `json:"quicOverride"` + SSLPolicy string `json:"sslPolicy"` + Region string `json:"region"` + ProxyBind bool `json:"proxyBind"` + ServerTLSPolicy string `json:"serverTlsPolicy"` + AuthorizationPolicy string `json:"authorizationPolicy"` + Fingerprint string `json:"fingerprint"` + Kind string `json:"kind"` +} + +type SInstanceGroup struct { + SResourceBase + region *SRegion + instances []SInstanceGroupInstance + + ID string `json:"id"` + CreationTimestamp string `json:"creationTimestamp"` + Description string `json:"description"` + NamedPorts []NamedPort `json:"namedPorts"` + Network string `json:"network"` + Fingerprint string `json:"fingerprint"` + Zone string `json:"zone"` + Size int64 `json:"size"` + Region string `json:"region"` + Subnetwork string `json:"subnetwork"` + Kind string `json:"kind"` +} + +type SInstanceGroupInstance struct { + instanceGroup *SInstanceGroup + Instance string `json:"instance"` + Status string `json:"status"` + NamedPorts []NamedPort `json:"namedPorts"` +} + +type NamedPort struct { + Name string `json:"name"` + Port int64 `json:"port"` +} + +type ConsistentHash struct { + HTTPCookie HTTPCookie `json:"httpCookie"` + MinimumRingSize string `json:"minimumRingSize"` +} + +type HTTPCookie struct { + Name string `json:"name"` + Path string `json:"path"` + TTL TTL `json:"ttl"` +} + +type TTL struct { + Seconds string `json:"seconds"` + Nanos int64 `json:"nanos"` +} + +type RouteAction struct { + WeightedBackendServices []WeightedBackendService `json:"weightedBackendServices"` + URLRewrite URLRewrite `json:"urlRewrite"` + Timeout MaxStreamDuration `json:"timeout"` + RetryPolicy RetryPolicy `json:"retryPolicy"` + RequestMirrorPolicy RequestMirrorPolicy `json:"requestMirrorPolicy"` + CorsPolicy CorsPolicy `json:"corsPolicy"` + FaultInjectionPolicy FaultInjectionPolicy `json:"faultInjectionPolicy"` + MaxStreamDuration MaxStreamDuration `json:"maxStreamDuration"` +} + +type CorsPolicy struct { + AllowOrigins []string `json:"allowOrigins"` + AllowOriginRegexes []string `json:"allowOriginRegexes"` + AllowMethods []string `json:"allowMethods"` + AllowHeaders []string `json:"allowHeaders"` + ExposeHeaders []string `json:"exposeHeaders"` + MaxAge int64 `json:"maxAge"` + AllowCredentials bool `json:"allowCredentials"` + Disabled bool `json:"disabled"` +} + +type FaultInjectionPolicy struct { + Delay Delay `json:"delay"` + Abort Abort `json:"abort"` +} + +type Abort struct { + HTTPStatus int64 `json:"httpStatus"` + Percentage float64 `json:"percentage"` +} + +type Delay struct { + FixedDelay MaxStreamDuration `json:"fixedDelay"` + Percentage float64 `json:"percentage"` +} + +type MaxStreamDuration struct { + Seconds string `json:"seconds"` + Nanos int64 `json:"nanos"` +} + +type RequestMirrorPolicy struct { + BackendService string `json:"backendService"` +} + +type RetryPolicy struct { + RetryConditions []string `json:"retryConditions"` + NumRetries int64 `json:"numRetries"` + PerTryTimeout MaxStreamDuration `json:"perTryTimeout"` +} + +type URLRewrite struct { + PathPrefixRewrite string `json:"pathPrefixRewrite"` + HostRewrite string `json:"hostRewrite"` +} + +type WeightedBackendService struct { + BackendService string `json:"backendService"` + Weight int64 `json:"weight"` + HeaderAction HeaderAction `json:"headerAction"` +} + +type HeaderAction struct { + RequestHeadersToRemove []string `json:"requestHeadersToRemove"` + RequestHeadersToAdd []HeadersToAdd `json:"requestHeadersToAdd"` + ResponseHeadersToRemove []string `json:"responseHeadersToRemove"` + ResponseHeadersToAdd []HeadersToAdd `json:"responseHeadersToAdd"` +} + +type HeadersToAdd struct { + HeaderName string `json:"headerName"` + HeaderValue string `json:"headerValue"` + Replace bool `json:"replace"` +} + +type URLRedirect struct { + HostRedirect string `json:"hostRedirect"` + PathRedirect string `json:"pathRedirect"` + PrefixRedirect string `json:"prefixRedirect"` + RedirectResponseCode string `json:"redirectResponseCode"` + HTTPSRedirect bool `json:"httpsRedirect"` + StripQuery bool `json:"stripQuery"` +} + +type HostRule struct { + Description string `json:"description"` + Hosts []string `json:"hosts"` + PathMatcher string `json:"pathMatcher"` +} + +type PathMatcher struct { + Name string `json:"name"` + Description string `json:"description"` + DefaultService string `json:"defaultService"` + DefaultRouteAction RouteAction `json:"defaultRouteAction"` + DefaultURLRedirect URLRedirect `json:"defaultUrlRedirect"` + PathRules []PathRule `json:"pathRules"` + RouteRules []RouteRule `json:"routeRules"` + HeaderAction HeaderAction `json:"headerAction"` +} + +type PathRule struct { + Service string `json:"service"` + RouteAction RouteAction `json:"routeAction"` + URLRedirect URLRedirect `json:"urlRedirect"` + Paths []string `json:"paths"` +} + +type RouteRule struct { + Priority int64 `json:"priority"` + Description string `json:"description"` + MatchRules []MatchRule `json:"matchRules"` + Service string `json:"service"` + RouteAction RouteAction `json:"routeAction"` + URLRedirect URLRedirect `json:"urlRedirect"` + HeaderAction HeaderAction `json:"headerAction"` +} + +type MatchRule struct { + PrefixMatch string `json:"prefixMatch"` + FullPathMatch string `json:"fullPathMatch"` + RegexMatch string `json:"regexMatch"` + IgnoreCase bool `json:"ignoreCase"` + HeaderMatches []HeaderMatch `json:"headerMatches"` + QueryParameterMatches []QueryParameterMatch `json:"queryParameterMatches"` + MetadataFilters []MetadataFilter `json:"metadataFilters"` +} + +type HeaderMatch struct { + HeaderName string `json:"headerName"` + ExactMatch string `json:"exactMatch"` + RegexMatch string `json:"regexMatch"` + RangeMatch RangeMatch `json:"rangeMatch"` + PresentMatch bool `json:"presentMatch"` + PrefixMatch string `json:"prefixMatch"` + SuffixMatch string `json:"suffixMatch"` + InvertMatch bool `json:"invertMatch"` +} + +type RangeMatch struct { + RangeStart string `json:"rangeStart"` + RangeEnd string `json:"rangeEnd"` +} + +type MetadataFilter struct { + FilterMatchCriteria string `json:"filterMatchCriteria"` + FilterLabels []Header `json:"filterLabels"` +} + +type Header struct { + Name string `json:"name"` + Value string `json:"value"` +} + +type QueryParameterMatch struct { + Name string `json:"name"` + PresentMatch bool `json:"presentMatch"` + ExactMatch string `json:"exactMatch"` + RegexMatch string `json:"regexMatch"` +} + +type Test struct { + Description string `json:"description"` + Host string `json:"host"` + Path string `json:"path"` + Headers []Header `json:"headers"` + Service string `json:"service"` + ExpectedOutputURL string `json:"expectedOutputUrl"` + ExpectedRedirectResponseCode int64 `json:"expectedRedirectResponseCode"` +} + +type Backend struct { + Group string `json:"group"` + BalancingMode string `json:"balancingMode"` + Failover bool `json:"failover"` +} + +type ConnectionDraining struct { + DrainingTimeoutSEC int64 `json:"drainingTimeoutSec"` +} + +type HealthChecks struct { + SResourceBase + + ID string `json:"id"` + CreationTimestamp string `json:"creationTimestamp"` + Description string `json:"description"` + CheckIntervalSEC int64 `json:"checkIntervalSec"` + TimeoutSEC int64 `json:"timeoutSec"` + UnhealthyThreshold int64 `json:"unhealthyThreshold"` + HealthyThreshold int64 `json:"healthyThreshold"` + Type string `json:"type"` + HTTPSHealthCheck HTTPHealthCheck `json:"httpsHealthCheck"` + Region string `json:"region"` + Kind string `json:"kind"` + Http2HealthCheck HTTPHealthCheck `json:"http2HealthCheck"` + TCPHealthCheck TCPHealthCheck `json:"tcpHealthCheck"` + SSLHealthCheck SSLHealthCheck `json:"sslHealthCheck"` + HTTPHealthCheck HTTPHealthCheck `json:"httpHealthCheck"` +} + +type HTTPHealthCheck struct { + Port int64 `json:"port"` + Host string `json:"host"` + RequestPath string `json:"requestPath"` + ProxyHeader string `json:"proxyHeader"` + Response string `json:"response"` +} + +type SSLHealthCheck struct { + Port int64 `json:"port"` + Request string `json:"request"` + Response string `json:"response"` + ProxyHeader string `json:"proxyHeader"` +} + +type TCPHealthCheck struct { + Port int64 `json:"port"` + ProxyHeader string `json:"proxyHeader"` +} + +type LogConfig struct { + Enable bool `json:"enable"` +} + +func (self *SInstanceGroup) GetInstances() ([]SInstanceGroupInstance, error) { + if self.instances != nil { + return self.instances, nil + } + + ret := make([]SInstanceGroupInstance, 0) + resourceId := strings.Replace(self.GetGlobalId(), fmt.Sprintf("projects/%s/", self.region.GetProjectId()), "", -1) + err := self.region.listAll("POST", resourceId+"/listInstances", nil, &ret) + if err != nil { + if errors.Cause(err) == cloudprovider.ErrNotFound { + return nil, nil + } + + return nil, errors.Wrap(err, "ListAll") + } + + for i := range ret { + ret[i].instanceGroup = self + } + + self.instances = ret + return ret, nil +} + +func (self *SRegion) getLoadbalancerComponents(resource string, filter string, result interface{}) error { + url := fmt.Sprintf("regions/%s/%s", self.Name, resource) + params := map[string]string{} + if len(filter) > 0 { + params["filter"] = filter + } + + err := self.ListAll(url, params, result) + if err != nil { + return errors.Wrap(err, "ListAll") + } + + return nil +} + +func (self *SRegion) getInstanceGroups(zoneId, resource string, filter string, result interface{}) error { + url := fmt.Sprintf("zones/%s/%s", zoneId, resource) + params := map[string]string{} + if len(filter) > 0 { + params["filter"] = filter + } + + err := self.ListAll(url, params, result) + if err != nil { + return errors.Wrap(err, "ListAll") + } + + return nil +} + +/* +As mentioned by Patrick W, there is no direct entity 'load balancer', its just a collection of components. +The list seen in the UI that appears to be the load balancer is actually the url-map component, +which can be seen via the API with: +gcloud compute url-maps list +*/ +func (self *SRegion) GetRegionalUrlMaps(filter string) ([]SUrlMap, error) { + ret := make([]SUrlMap, 0) + err := self.getLoadbalancerComponents("urlMaps", filter, &ret) + return ret, err +} + +func (self *SRegion) GetRegionalBackendServices(filter string) ([]SBackendServices, error) { + ret := make([]SBackendServices, 0) + err := self.getLoadbalancerComponents("backendServices", filter, &ret) + return ret, err +} + +func (self *SRegion) GetRegionalForwardingRule(filter string) ([]SForwardingRule, error) { + ret := make([]SForwardingRule, 0) + err := self.getLoadbalancerComponents("forwardingRules", filter, &ret) + return ret, err +} + +func (self *SRegion) GetRegionalTargetHttpProxies(filter string) ([]STargetHttpProxy, error) { + ret := make([]STargetHttpProxy, 0) + err := self.getLoadbalancerComponents("targetHttpProxies", filter, &ret) + return ret, err +} + +func (self *SRegion) GetRegionalTargetHttpsProxies(filter string) ([]STargetHttpsProxy, error) { + ret := make([]STargetHttpsProxy, 0) + err := self.getLoadbalancerComponents("targetHttpsProxies", filter, &ret) + return ret, err +} + +func (self *SRegion) GetRegionalInstanceGroups(filter string) ([]SInstanceGroup, error) { + ret := make([]SInstanceGroup, 0) + err := self.getLoadbalancerComponents("instanceGroups", filter, &ret) + for i := range ret { + ret[i].region = self + } + return ret, err +} + +func (self *SRegion) GetRegionalHealthChecks(filter string) ([]HealthChecks, error) { + ret := make([]HealthChecks, 0) + err := self.getLoadbalancerComponents("healthChecks", filter, &ret) + return ret, err +} + +func (self *SRegion) GetGlobalHealthChecks(filter string) ([]HealthChecks, error) { + ret := make([]HealthChecks, 0) + params := map[string]string{} + if len(filter) > 0 { + params["filter"] = filter + } + + err := self.ListAll("global/healthChecks", params, &ret) + if err != nil { + return nil, errors.Wrap(err, "ListAll") + } + + return ret, err +} + +func (self *SRegion) GetRegionalSslCertificates(filter string) ([]SLoadbalancerCertificate, error) { + ret := make([]SLoadbalancerCertificate, 0) + err := self.getLoadbalancerComponents("sslCertificates", filter, &ret) + for i := range ret { + ret[i].region = self + } + return ret, err +} + +func (self *SLoadbalancer) GetTargetHttpsProxies() ([]STargetHttpsProxy, error) { + ret := make([]STargetHttpsProxy, 0) + filter := fmt.Sprintf("urlMap eq %s", self.GetId()) + err := self.region.getLoadbalancerComponents("targetHttpsProxies", filter, &ret) + return ret, err +} + +func (self *SLoadbalancer) GetTargetHttpProxies() ([]STargetHttpProxy, error) { + ret := make([]STargetHttpProxy, 0) + filter := fmt.Sprintf("urlMap eq %s", self.GetId()) + err := self.region.getLoadbalancerComponents("targetHttpProxies", filter, &ret) + return ret, err +} + +// ForwardingRule 目标是: target proxy or backend service +// http&https 是由target proxy 转发到后端服务 +func (self *SLoadbalancer) GetForwardingRules() ([]SForwardingRule, error) { + if self.forwardRules != nil { + return self.forwardRules, nil + } + + hps, err := self.GetTargetHttpProxies() + if err != nil { + return nil, errors.Wrap(err, "GetTargetHttpProxies") + } + + hsps, err := self.GetTargetHttpsProxies() + if err != nil { + return nil, errors.Wrap(err, "GetTargetHttpsProxies") + } + + targets := make([]string, 0) + for i := range hps { + targets = append(targets, fmt.Sprintf(`(target="%s")`, hps[i].GetId())) + } + + for i := range hsps { + targets = append(targets, fmt.Sprintf(`(target="%s")`, hsps[i].GetId())) + } + + if strings.Contains(self.GetId(), "/backendServices/") { + targets = append(targets, fmt.Sprintf(`(backendService="%s")`, self.GetId())) + } + + if len(targets) == 0 { + return []SForwardingRule{}, nil + } + + filter := strings.Join(targets, " OR ") + ret := make([]SForwardingRule, 0) + err = self.region.getLoadbalancerComponents("forwardingRules", filter, &ret) + if err != nil { + return nil, errors.Wrap(err, "GetForwardingRules") + } + + if len(ret) > 0 { + self.forwardRules = ret + } + return ret, nil +} + +func (self *SLoadbalancer) GetBackendServices() ([]SBackendServices, error) { + if self.isHttpLb && self.urlMap != nil { + ret := make([]SBackendServices, 0) + ids := []string{self.urlMap.DefaultService} + for i := range self.urlMap.PathMatchers { + ps := self.urlMap.PathMatchers[i] + if len(ps.DefaultService) > 0 && !utils.IsInStringArray(ps.DefaultService, ids) { + ids = append(ids, ps.DefaultService) + } + + for j := range ps.PathRules { + if len(ps.PathRules[j].Service) > 0 && !utils.IsInStringArray(ps.PathRules[j].Service, ids) { + ids = append(ids, ps.PathRules[j].Service) + } + } + } + + filters := []string{} + for i := range ids { + filters = append(filters, fmt.Sprintf(`(selfLink="%s")`, ids[i])) + } + + if len(filters) == 0 { + return []SBackendServices{}, nil + } + err := self.region.getLoadbalancerComponents("backendServices", strings.Join(filters, " OR "), &ret) + self.backendServices = ret + return ret, err + } + + return self.backendServices, nil +} + +func (self *SLoadbalancer) GetInstanceGroupsMap() (map[string]SInstanceGroup, error) { + igs, err := self.GetInstanceGroups() + if err != nil { + return nil, errors.Wrap(err, "GetInstanceGroups") + } + + ret := make(map[string]SInstanceGroup, 0) + for i := range igs { + ig := igs[i] + ig.region = self.region + ret[ig.SelfLink] = ig + } + + return ret, nil +} + +func (self *SLoadbalancer) GetInstanceGroups() ([]SInstanceGroup, error) { + if self.instanceGroups != nil { + return self.instanceGroups, nil + } + + if self.backendServices == nil { + bss, err := self.GetBackendServices() + if err != nil { + return nil, errors.Wrap(err, "GetBackendServices") + } + self.backendServices = bss + } + + bgs := []string{} + for i := range self.backendServices { + _bgs := self.backendServices[i].Backends + for j := range _bgs { + if !utils.IsInStringArray(_bgs[j].Group, bgs) { + bgs = append(bgs, _bgs[j].Group) + } + } + } + + if len(bgs) == 0 { + return []SInstanceGroup{}, nil + } + + regionFilters := []string{} + zonesFilter := map[string][]string{} + for i := range bgs { + if !strings.Contains(bgs[i], "/zones/") { + regionFilters = append(regionFilters, fmt.Sprintf(`(selfLink="%s")`, bgs[i])) + } else { + ig := bgs[i] + index := strings.Index(ig, "/zones/") + zoneId := strings.Split(ig[index:], "/")[2] + if fs, ok := zonesFilter[zoneId]; ok { + f := fmt.Sprintf(`(selfLink="%s")`, ig) + if !utils.IsInStringArray(f, fs) { + fs = append(fs, f) + } + } else { + fs = []string{fmt.Sprintf(`(selfLink="%s")`, ig)} + zonesFilter[zoneId] = fs + } + } + } + + igs := make([]SInstanceGroup, 0) + // regional instance groups + if len(regionFilters) > 0 { + _igs, err := self.region.GetRegionalInstanceGroups(strings.Join(regionFilters, " OR ")) + if err != nil { + return nil, errors.Wrap(err, "GetRegionalInstanceGroups") + } + + igs = append(igs, _igs...) + } + + for z, fs := range zonesFilter { + _igs := make([]SInstanceGroup, 0) + err := self.region.getInstanceGroups(z, "instanceGroups", strings.Join(fs, " OR "), &_igs) + if err != nil { + return nil, errors.Wrap(err, "getInstanceGroups") + } + + igs = append(igs, _igs...) + } + + self.instanceGroups = igs + return igs, nil +} + +func (self *SLoadbalancer) GetHealthChecks() ([]HealthChecks, error) { + if self.healthChecks != nil { + return self.healthChecks, nil + } + + hcs, err := self.region.GetRegionalHealthChecks("") + if err != nil { + return nil, errors.Wrap(err, "GetRegionalHealthChecks") + } + + ghcs, err := self.region.GetGlobalHealthChecks("") + if err != nil { + return nil, errors.Wrap(err, "GetGlobalHealthChecks") + } + + self.healthChecks = append(self.healthChecks, ghcs...) + self.healthChecks = append(self.healthChecks, hcs...) + return self.healthChecks, err +} + +func (self *SLoadbalancer) GetHealthCheckMaps() (map[string]HealthChecks, error) { + hcs, err := self.GetHealthChecks() + if err != nil { + return nil, errors.Wrap(err, "GetHealthChecks") + } + + ret := map[string]HealthChecks{} + for i := range hcs { + ret[hcs[i].SelfLink] = hcs[i] + } + return ret, err +} diff --git a/pkg/multicloud/google/loadbalancer_listener.go b/pkg/multicloud/google/loadbalancer_listener.go new file mode 100644 index 0000000000..50700008b2 --- /dev/null +++ b/pkg/multicloud/google/loadbalancer_listener.go @@ -0,0 +1,573 @@ +package google + +import ( + "context" + "fmt" + "strconv" + "strings" + + "yunion.io/x/log" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SLoadbalancerListener struct { + lb *SLoadbalancer + rules []SLoadbalancerListenerRule + forwardRule SForwardingRule // 服务IP地址 + backendService SBackendServices // + httpProxy *STargetHttpProxy // http + httpsProxy *STargetHttpsProxy // https + healthChecks []HealthChecks + + ForwardRuleName string `json:"forward_rule_name"` + BackendServiceName string `json:"backend_service_name"` + Protocol string `json:"protocol"` + Port string `json:"port"` // 监听端口 +} + +func (self *SLoadbalancerListener) GetId() string { + return fmt.Sprintf("%s::%s::%s", self.forwardRule.GetGlobalId(), self.backendService.GetGlobalId(), self.Port) +} + +func (self *SLoadbalancerListener) GetName() string { + return fmt.Sprintf("%s::%s::%s", self.forwardRule.GetName(), self.backendService.GetName(), self.Port) +} + +func (self *SLoadbalancerListener) GetGlobalId() string { + return self.GetId() +} + +func (self *SLoadbalancerListener) GetStatus() string { + return api.LB_STATUS_ENABLED +} + +func (self *SLoadbalancerListener) Refresh() error { + return nil +} + +func (self *SLoadbalancerListener) IsEmulated() bool { + return true +} + +func (self *SLoadbalancerListener) GetSysTags() map[string]string { + return nil +} + +func (self *SLoadbalancerListener) GetTags() (map[string]string, error) { + if len(self.forwardRule.IPAddress) > 0 { + return map[string]string{"FrontendIP": self.forwardRule.IPAddress}, nil + } + + return map[string]string{}, nil +} + +func (self *SLoadbalancerListener) SetTags(tags map[string]string, replace bool) error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadbalancerListener) GetProjectId() string { + return self.lb.GetProjectId() +} + +func (self *SLoadbalancerListener) GetListenerType() string { + return self.Protocol +} + +func (self *SLoadbalancerListener) GetListenerPort() int { + port, err := strconv.Atoi(self.Port) + if err != nil { + log.Errorf("GetListenerPort %s", err) + return 0 + } + + return port +} + +/* +在本地范围内使用的负载均衡算法。可能的值为: + +ROUND_ROBIN:这是一个简单的策略,其中按循环顺序选择每个健康的后端。这是默认设置。 +LEAST_REQUEST:一种 O(1) 算法,它选择两个随机的健康主机并选择具有较少活动请求的主机。 +RING_HASH:环/模散列负载均衡器对后端实现一致的散列。该算法的特性是从一组 N 个主机中添加/删除一个主机只会影响 1/N 的请求。 +RANDOM:负载均衡器随机选择一个健康的主机。 +ORIGINAL_DESTINATION:根据客户端连接元数据选择后端主机,即在连接被重定向到负载均衡器之前,连接被打开到与传入连接的目标地址相同的地址。 +MAGLEV:用作环形哈希负载均衡器的替代品。Maglev 不如环哈希稳定,但具有更快的表查找构建时间和主机选择时间。有关磁悬浮的更多信息,请参阅https://ai.google/research/pubs/pub44824 +此字段适用于: + +service_protocol 设置为 HTTP、HTTPS 或 HTTP2,并且 load_balancing_scheme 设置为 INTERNAL_MANAGED 的区域后端服务。 +load_balancing_scheme 设置为 INTERNAL_SELF_MANAGED 的全局后端服务。 +如果 sessionAffinity 不为 NONE,并且该字段未设置为 MAGLEV 或 RING_HASH,则会话亲缘性设置不会生效。 + +当后端服务被绑定到目标 gRPC 代理且 validateForProxyless 字段设置为 true 的 URL 映射引用时,仅支持默认的 ROUND_ROBIN 策略。 +*/ +// todo: fix me ??? +func (self *SLoadbalancerListener) GetScheduler() string { + switch self.backendService.LocalityLBPolicy { + case "ROUND_ROBIN": + return api.LB_SCHEDULER_RR + case "LEAST_REQUEST": + return api.LB_SCHEDULER_WLC + case "RING_HASH": + return api.LB_SCHEDULER_QCH + case "ORIGINAL_DESTINATION": + return api.LB_SCHEDULER_SCH + case "MAGLEV": + return api.LB_SCHEDULER_MH + default: + return "" + } +} + +func (self *SLoadbalancerListener) GetAclStatus() string { + return api.LB_BOOL_OFF +} + +func (self *SLoadbalancerListener) GetAclType() string { + return "" +} + +func (self *SLoadbalancerListener) GetAclId() string { + return "" +} + +func (self *SLoadbalancerListener) GetEgressMbps() int { + return 0 +} + +func (self *SLoadbalancerListener) GetBackendGroupId() string { + return self.backendService.GetGlobalId() +} + +func (self *SLoadbalancerListener) GetBackendServerPort() int { + igs, err := self.GetInstanceGroups() + if err != nil { + log.Errorf("GetInstanceGroups %s", err) + return 0 + } + + for i := range igs { + for j := range igs[i].NamedPorts { + if igs[i].NamedPorts[j].Name == self.backendService.PortName { + return int(igs[i].NamedPorts[j].Port) + } + } + } + + return 0 +} + +func (self *SLoadbalancerListener) GetClientIdleTimeout() int { + return int(self.backendService.ConnectionDraining.DrainingTimeoutSEC) +} + +func (self *SLoadbalancerListener) GetBackendConnectTimeout() int { + return int(self.backendService.TimeoutSEC) +} + +func (self *SLoadbalancerListener) CreateILoadBalancerListenerRule(rule *cloudprovider.SLoadbalancerListenerRule) (cloudprovider.ICloudLoadbalancerListenerRule, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SLoadbalancerListener) GetILoadBalancerListenerRuleById(ruleId string) (cloudprovider.ICloudLoadbalancerListenerRule, error) { + rules, err := self.GetLoadbalancerListenerRules() + if err != nil { + return nil, errors.Wrap(err, "GetLoadbalancerListenerRules") + } + + for i := range rules { + if rules[i].GetGlobalId() == ruleId { + return &rules[i], nil + } + } + + return nil, cloudprovider.ErrNotFound +} + +func (self *SLoadbalancerListener) GetILoadbalancerListenerRules() ([]cloudprovider.ICloudLoadbalancerListenerRule, error) { + rules, err := self.GetLoadbalancerListenerRules() + if err != nil { + return nil, errors.Wrap(err, "GetLoadbalancerListenerRules") + } + + irules := make([]cloudprovider.ICloudLoadbalancerListenerRule, len(rules)) + for i := range rules { + irules[i] = &rules[i] + } + + return irules, nil +} + +func (self *SLoadbalancerListener) GetStickySession() string { + if self.backendService.SessionAffinity == "NONE" { + return api.LB_BOOL_OFF + } else { + return api.LB_BOOL_ON + } +} + +/* +https://cloud.google.com/load-balancing/docs/backend-service#sessionAffinity +区域级外部 HTTP(S) 负载均衡器: + 无 (NONE) + 客户端 IP (CLIENT_IP) + 生成的 Cookie (GENERATED_COOKIE) + 标头字段 (HEADER_FIELD) + HTTP Cookie (HTTP_COOKIE) +*/ +func (self *SLoadbalancerListener) GetStickySessionType() string { + switch self.backendService.SessionAffinity { + case "HTTP_COOKIE": + return api.LB_STICKY_SESSION_TYPE_SERVER + case "GENERATED_COOKIE": + return api.LB_STICKY_SESSION_TYPE_INSERT + } + return self.backendService.SessionAffinity +} + +func (self *SLoadbalancerListener) GetStickySessionCookie() string { + return self.backendService.ConsistentHash.HTTPCookie.Name +} + +func (self *SLoadbalancerListener) GetStickySessionCookieTimeout() int { + sec, err := strconv.Atoi(self.backendService.ConsistentHash.HTTPCookie.TTL.Seconds) + if err != nil { + log.Debugf("GetStickySessionCookieTimeout %s", err) + return 0 + } + + return sec +} + +// https://cloud.google.com/load-balancing/docs/https +func (self *SLoadbalancerListener) XForwardedForEnabled() bool { + return true +} + +// https://cloud.google.com/load-balancing/docs/https/troubleshooting-ext-https-lbs +func (self *SLoadbalancerListener) GzipEnabled() bool { + return false +} + +func (self *SLoadbalancerListener) GetCertificateId() string { + if self.httpsProxy != nil && len(self.httpsProxy.SSLCertificates) > 0 { + cert := SResourceBase{ + Name: "", + SelfLink: self.httpsProxy.SSLCertificates[0], + } + return cert.GetGlobalId() + } + + return "" +} + +func (self *SLoadbalancerListener) GetTLSCipherPolicy() string { + return "" +} + +// https://cloud.google.com/load-balancing/docs/https/troubleshooting-ext-https-lbs +func (self *SLoadbalancerListener) HTTP2Enabled() bool { + return true +} + +// todo: fix me route +// 高级配置才有重定向,具体怎么解析? +func (self *SLoadbalancerListener) GetRedirect() string { + //self.lb.urlMap.PathMatchers[0].RouteRules[0].URLRedirect + return "" +} + +func (self *SLoadbalancerListener) GetRedirectCode() int64 { + return 0 +} + +func (self *SLoadbalancerListener) GetRedirectScheme() string { + return "" +} + +func (self *SLoadbalancerListener) GetRedirectHost() string { + return "" +} + +func (self *SLoadbalancerListener) GetRedirectPath() string { + return "" +} + +func (self *SLoadbalancerListener) GetHealthCheck() string { + if len(self.backendService.HealthChecks) > 0 { + return api.LB_BOOL_ON + } else { + return api.LB_BOOL_OFF + } +} + +func (self *SLoadbalancerListener) GetHealthChecks() []HealthChecks { + if self.healthChecks != nil { + return self.healthChecks + } + + hcm, err := self.lb.GetHealthCheckMaps() + if err != nil { + log.Errorf("GetHealthCheckMaps %s", err) + return nil + } + ret := make([]HealthChecks, 0) + for i := range self.backendService.HealthChecks { + hc := self.backendService.HealthChecks[i] + if _, ok := hcm[hc]; ok { + ret = append(ret, hcm[hc]) + } + } + + self.healthChecks = ret + return ret +} + +func (self *SLoadbalancerListener) GetHealthCheckType() string { + hcs := self.GetHealthChecks() + if hcs == nil { + return "" + } + + switch strings.ToLower(hcs[0].Type) { + case "tcp": + return api.LB_HEALTH_CHECK_TCP + case "udp": + return api.LB_HEALTH_CHECK_UDP + case "http", "http2": + return api.LB_HEALTH_CHECK_HTTP + case "https", "ssl": + return api.LB_HEALTH_CHECK_HTTPS + default: + return "" + } +} + +func (self *SLoadbalancerListener) GetHealthCheckTimeout() int { + hcs := self.GetHealthChecks() + if hcs == nil { + return 0 + } + + return int(hcs[0].TimeoutSEC) +} + +func (self *SLoadbalancerListener) GetHealthCheckInterval() int { + hcs := self.GetHealthChecks() + if hcs == nil { + return 0 + } + + return int(hcs[0].CheckIntervalSEC) +} + +func (self *SLoadbalancerListener) GetHealthCheckRise() int { + hcs := self.GetHealthChecks() + if hcs == nil { + return 0 + } + + return int(hcs[0].HealthyThreshold) +} + +func (self *SLoadbalancerListener) GetHealthCheckFail() int { + hcs := self.GetHealthChecks() + if hcs == nil { + return 0 + } + + return int(hcs[0].UnhealthyThreshold) +} + +func (self *SLoadbalancerListener) GetHealthCheckReq() string { + return "" +} + +func (self *SLoadbalancerListener) GetHealthCheckExp() string { + return "" +} + +func (self *SLoadbalancerListener) GetHealthCheckDomain() string { + hcs := self.GetHealthChecks() + if hcs == nil { + return "" + } + switch hcs[0].Type { + case "HTTPS": + return hcs[0].HTTPSHealthCheck.Host + case "HTTP2": + return hcs[0].Http2HealthCheck.Host + case "HTTP": + return hcs[0].HTTPHealthCheck.Host + default: + return "" + } +} + +func (self *SLoadbalancerListener) GetHealthCheckURI() string { + hcs := self.GetHealthChecks() + if hcs == nil { + return "" + } + switch hcs[0].Type { + case "HTTPS": + return hcs[0].HTTPSHealthCheck.RequestPath + case "HTTP2": + return hcs[0].Http2HealthCheck.RequestPath + case "HTTP": + return hcs[0].HTTPHealthCheck.RequestPath + default: + return "" + } +} + +func (self *SLoadbalancerListener) GetHealthCheckCode() string { + return "" +} + +func (self *SLoadbalancerListener) Start() error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadbalancerListener) Stop() error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadbalancerListener) Sync(ctx context.Context, listener *cloudprovider.SLoadbalancerListener) error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadbalancerListener) Delete(ctx context.Context) error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadbalancerListener) GetInstanceGroups() ([]SInstanceGroup, error) { + igs, err := self.lb.GetInstanceGroupsMap() + if err != nil { + return nil, errors.Wrap(err, "GetInstanceGroups") + } + + ret := make([]SInstanceGroup, 0) + for i := range self.backendService.Backends { + b := self.backendService.Backends[i] + if ig, ok := igs[b.Group]; ok { + ret = append(ret, ig) + } + } + + return ret, nil +} + +func (self *SLoadbalancer) GetLoadbalancerListeners() ([]SLoadbalancerListener, error) { + if self.urlMap != nil { + return self.GetHTTPLoadbalancerListeners() + } else { + return self.GetNetworkLoadbalancerListeners() + } +} + +func (self *SLoadbalancer) GetHTTPLoadbalancerListeners() ([]SLoadbalancerListener, error) { + frs, err := self.GetForwardingRules() + if err != nil { + return nil, errors.Wrap(err, "GetForwardingRules") + } + + _hps, err := self.GetTargetHttpProxies() + if err != nil { + return nil, errors.Wrap(err, "GetTargetHttpProxies") + } + + hps := make(map[string]STargetHttpProxy, 0) + for i := range _hps { + hps[_hps[i].SelfLink] = _hps[i] + } + + _hsps, err := self.GetTargetHttpsProxies() + if err != nil { + return nil, errors.Wrap(err, "GetTargetHttpsProxies") + } + + hsps := make(map[string]STargetHttpsProxy, 0) + for i := range _hsps { + hsps[_hsps[i].SelfLink] = _hsps[i] + } + + bss, err := self.GetBackendServices() + if err != nil { + return nil, errors.Wrap(err, "GetBackendServices") + } + + lbls := make([]SLoadbalancerListener, 0) + for i := range frs { + fr := frs[i] + for j := range bss { + bs := bss[j] + port := "80" + protocol := "http" + var hp STargetHttpProxy + var hsp STargetHttpsProxy + if fr.PortRange == "443-443" { + port = "443" + hsp = hsps[fr.Target] + protocol = "https" + } else if fr.PortRange == "8080-8080" { + port = "8080" + hp = hps[fr.Target] + } else { + hp = hps[fr.Target] + } + + lbl := SLoadbalancerListener{ + lb: self, + forwardRule: fr, + backendService: bs, + httpProxy: &hp, + httpsProxy: &hsp, + ForwardRuleName: fr.GetName(), + BackendServiceName: bs.GetName(), + Protocol: protocol, + Port: port, + } + lbls = append(lbls, lbl) + } + } + + return lbls, nil +} + +func (self *SLoadbalancer) GetNetworkLoadbalancerListeners() ([]SLoadbalancerListener, error) { + frs, err := self.GetForwardingRules() + if err != nil { + return nil, errors.Wrap(err, "GetForwardingRules") + } + + bss, err := self.GetBackendServices() + if err != nil { + return nil, errors.Wrap(err, "GetBackendServices") + } + + lbls := make([]SLoadbalancerListener, 0) + for i := range frs { + fr := frs[i] + for j := range bss { + bs := bss[j] + for n := range fr.Ports { + lbl := SLoadbalancerListener{ + lb: self, + forwardRule: fr, + backendService: bs, + ForwardRuleName: fr.GetName(), + BackendServiceName: bs.GetName(), + Protocol: strings.ToLower(fr.IPProtocol), + Port: fr.Ports[n], + } + + lbls = append(lbls, lbl) + } + } + } + + return lbls, nil +} diff --git a/pkg/multicloud/google/loadbalancer_listenerrule.go b/pkg/multicloud/google/loadbalancer_listenerrule.go new file mode 100644 index 0000000000..d872c64ffa --- /dev/null +++ b/pkg/multicloud/google/loadbalancer_listenerrule.go @@ -0,0 +1,163 @@ +package google + +import ( + "context" + "fmt" + "strings" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SLoadbalancerListenerRule struct { + lbl *SLoadbalancerListener + pathMatcher PathMatcher + pathRule PathRule + backendService SBackendServices + + ListenerName string `json:"listener_name"` + BackendServiceName string `json:"backend_service_name"` + Domain string `json:"domain"` + Path string `json:"path"` + Port string `json:"Port"` +} + +func (self *SLoadbalancerListenerRule) GetId() string { + return fmt.Sprintf("%s::%s::%s", self.lbl.GetGlobalId(), self.Domain, strings.Join(self.pathRule.Paths, ",")) +} + +func (self *SLoadbalancerListenerRule) GetName() string { + return fmt.Sprintf("%s::%s::%s", self.lbl.GetName(), self.Domain, strings.Join(self.pathRule.Paths, ",")) +} + +func (self *SLoadbalancerListenerRule) GetGlobalId() string { + return self.GetId() +} + +func (self *SLoadbalancerListenerRule) GetStatus() string { + return api.LB_STATUS_ENABLED +} + +func (self *SLoadbalancerListenerRule) Refresh() error { + return nil +} + +func (self *SLoadbalancerListenerRule) IsEmulated() bool { + return true +} + +func (self *SLoadbalancerListenerRule) GetSysTags() map[string]string { + return nil +} + +func (self *SLoadbalancerListenerRule) GetTags() (map[string]string, error) { + return nil, nil +} + +func (self *SLoadbalancerListenerRule) SetTags(tags map[string]string, replace bool) error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadbalancerListenerRule) GetProjectId() string { + return self.lbl.GetProjectId() +} + +func (self *SLoadbalancerListenerRule) GetRedirect() string { + return "" +} + +func (self *SLoadbalancerListenerRule) GetRedirectCode() int64 { + return 0 +} + +func (self *SLoadbalancerListenerRule) GetRedirectScheme() string { + return "" +} + +func (self *SLoadbalancerListenerRule) GetRedirectHost() string { + return "" +} + +func (self *SLoadbalancerListenerRule) GetRedirectPath() string { + return "" +} + +func (self *SLoadbalancerListenerRule) IsDefault() bool { + return false +} + +func (self *SLoadbalancerListenerRule) GetDomain() string { + return self.Domain +} + +func (self *SLoadbalancerListenerRule) GetPath() string { + return self.Path +} + +func (self *SLoadbalancerListenerRule) GetCondition() string { + return "" +} + +func (self *SLoadbalancerListenerRule) GetBackendGroupId() string { + return self.backendService.GetGlobalId() +} + +func (self *SLoadbalancerListenerRule) Delete(ctx context.Context) error { + return cloudprovider.ErrNotSupported +} + +func (self *SLoadbalancerListener) GetLoadbalancerListenerRules() ([]SLoadbalancerListenerRule, error) { + if !self.lb.isHttpLb { + return nil, nil + } + + if self.rules != nil { + return self.rules, nil + } + + hostRules := self.lb.urlMap.HostRules + pathMatchers := self.lb.urlMap.PathMatchers + + pmm := make(map[string]PathMatcher, 0) + for i := range pathMatchers { + name := pathMatchers[i].Name + pmm[name] = pathMatchers[i] + } + + ret := make([]SLoadbalancerListenerRule, 0) + for _, rule := range hostRules { + pm, ok := pmm[rule.PathMatcher] + if !ok { + continue + } + + for i := range rule.Hosts { + host := rule.Hosts[i] + for j := range pm.PathRules { + pr := pm.PathRules[j] + + if pr.Service != self.backendService.GetId() { + continue + } + + r := SLoadbalancerListenerRule{ + lbl: self, + backendService: self.backendService, + BackendServiceName: self.backendService.GetName(), + pathMatcher: pm, + pathRule: pr, + + ListenerName: self.GetName(), + Domain: host, + Path: strings.Join(pr.Paths, ","), + Port: self.Port, + } + + ret = append(ret, r) + } + } + } + + self.rules = ret + return ret, nil +} diff --git a/pkg/multicloud/google/region.go b/pkg/multicloud/google/region.go index 98110e8770..226e084198 100644 --- a/pkg/multicloud/google/region.go +++ b/pkg/multicloud/google/region.go @@ -500,7 +500,11 @@ func (region *SRegion) BillingListAll(resource string, params map[string]string, } func (region *SRegion) ListAll(resource string, params map[string]string, retval interface{}) error { - return region.client.ecsListAll(resource, params, retval) + return region.listAll("GET", resource, params, retval) +} + +func (region *SRegion) listAll(method string, resource string, params map[string]string, retval interface{}) error { + return region.client._ecsListAll(method, resource, params, retval) } func (region *SRegion) List(resource string, params map[string]string, maxResults int, pageToken string, retval interface{}) error { diff --git a/pkg/multicloud/google/shell/loadbalancer.go b/pkg/multicloud/google/shell/loadbalancer.go new file mode 100644 index 0000000000..b8c9a311fd --- /dev/null +++ b/pkg/multicloud/google/shell/loadbalancer.go @@ -0,0 +1,214 @@ +// Copyright 2019 Yunion +// +// 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 shell + +import ( + "yunion.io/x/onecloud/pkg/multicloud/google" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type ElbListOptions struct { + } + shellutils.R(&ElbListOptions{}, "lb-list", "List loadbalancers", func(cli *google.SRegion, args *ElbListOptions) error { + elbs, err := cli.GetRegionalLoadbalancers() + if err != nil { + return err + } + + printList(elbs, len(elbs), 0, 0, []string{}) + return nil + }) + + type ElbShowOptions struct { + RESOURCEID string `json:"resourceid"` + } + shellutils.R(&ElbShowOptions{}, "lb-bss", "List all loadbalancer backend services", func(cli *google.SRegion, args *ElbShowOptions) error { + lb, err := cli.GetLoadbalancer(args.RESOURCEID) + if err != nil { + return err + } + + elbs, err := lb.GetBackendServices() + if err != nil { + return err + } + + printList(elbs, len(elbs), 0, 0, []string{}) + return nil + }) + + shellutils.R(&ElbShowOptions{}, "lb-frs", "List all loadbalancer forward rules", func(cli *google.SRegion, args *ElbShowOptions) error { + lb, err := cli.GetLoadbalancer(args.RESOURCEID) + if err != nil { + return err + } + + elbs, err := lb.GetForwardingRules() + if err != nil { + return err + } + + printList(elbs, len(elbs), 0, 0, []string{}) + return nil + }) + + shellutils.R(&ElbShowOptions{}, "lb-http", "List loadbalancer https proxies", func(cli *google.SRegion, args *ElbShowOptions) error { + elb, err := cli.GetLoadbalancer(args.RESOURCEID) + if err != nil { + return err + } + + hps, e := elb.GetTargetHttpProxies() + if e != nil { + return e + } + printList(hps, len(hps), 0, 0, []string{}) + return nil + }) + + shellutils.R(&ElbShowOptions{}, "lb-https", "List loadbalancer https proxies", func(cli *google.SRegion, args *ElbShowOptions) error { + elb, err := cli.GetLoadbalancer(args.RESOURCEID) + if err != nil { + return err + } + + hps, e := elb.GetTargetHttpsProxies() + if e != nil { + return e + } + printList(hps, len(hps), 0, 0, []string{}) + return nil + }) + + shellutils.R(&ElbShowOptions{}, "lb-igs", "List all loadbalancer instance groups", func(cli *google.SRegion, args *ElbShowOptions) error { + elb, err := cli.GetLoadbalancer(args.RESOURCEID) + if err != nil { + return err + } + + hps, e := elb.GetInstanceGroups() + if e != nil { + return e + } + printList(hps, len(hps), 0, 0, []string{}) + return nil + }) + + shellutils.R(&ElbShowOptions{}, "lbl-list", "List all loadbalancer listeners", func(cli *google.SRegion, args *ElbShowOptions) error { + elb, err := cli.GetLoadbalancer(args.RESOURCEID) + if err != nil { + return err + } + + hps, e := elb.GetLoadbalancerListeners() + if e != nil { + return e + } + printList(hps, len(hps), 0, 0, []string{}) + return nil + }) + + shellutils.R(&ElbShowOptions{}, "lblr-list", "List all loadbalancer listener rules", func(cli *google.SRegion, args *ElbShowOptions) error { + elb, err := cli.GetLoadbalancer(args.RESOURCEID) + if err != nil { + return err + } + + hps, e := elb.GetLoadbalancerListeners() + if e != nil { + return e + } + + rules := make([]google.SLoadbalancerListenerRule, 0) + + for i := range hps { + _rules, ee := hps[i].GetLoadbalancerListenerRules() + if e != nil { + return ee + } + + rules = append(rules, _rules...) + } + + printList(rules, len(rules), 0, 0, []string{}) + return nil + }) + + shellutils.R(&ElbShowOptions{}, "lbbg-list", "List all loadbalancer backendgroups", func(cli *google.SRegion, args *ElbShowOptions) error { + lb, err := cli.GetLoadbalancer(args.RESOURCEID) + if err != nil { + return err + } + + lbbg, e := lb.GetLoadbalancerBackendGroups() + if e != nil { + return e + } + + printList(lbbg, len(lbbg), 0, 0, []string{}) + return nil + }) + + shellutils.R(&ElbShowOptions{}, "lbb-list", "List all loadbalancer backends", func(cli *google.SRegion, args *ElbShowOptions) error { + lb, err := cli.GetLoadbalancer(args.RESOURCEID) + if err != nil { + return err + } + + lbbg, e := lb.GetLoadbalancerBackendGroups() + if e != nil { + return e + } + + lbbs := make([]google.SLoadbalancerBackend, 0) + for i := range lbbg { + backends, err := lbbg[i].GetLoadbalancerBackends() + if err != nil { + return err + } + + lbbs = append(lbbs, backends...) + } + + printList(lbbs, len(lbbs), 0, 0, []string{}) + return nil + }) + + shellutils.R(&ElbShowOptions{}, "lbhc-list", "List all loadbalancer health checks", func(cli *google.SRegion, args *ElbShowOptions) error { + lb, err := cli.GetLoadbalancer(args.RESOURCEID) + if err != nil { + return err + } + + hcs, e := lb.GetHealthChecks() + if e != nil { + return e + } + + printList(hcs, len(hcs), 0, 0, []string{}) + return nil + }) + + shellutils.R(&ElbListOptions{}, "cert-list", "List region certificates", func(cli *google.SRegion, args *ElbListOptions) error { + certs, err := cli.GetRegionalSslCertificates("") + if err != nil { + return err + } + + printList(certs, len(certs), 0, 0, []string{}) + return nil + }) +}