blob: e852c2e87ea12cba72ee81030baaa510446f5ca5 [file] [log] [blame]
/*
* 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 disco
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"github.com/apache/servicecomb-service-center/datasource"
"github.com/apache/servicecomb-service-center/pkg/log"
"github.com/apache/servicecomb-service-center/pkg/rest"
"github.com/apache/servicecomb-service-center/pkg/util"
discosvc "github.com/apache/servicecomb-service-center/server/service/disco"
pb "github.com/go-chassis/cari/discovery"
)
var trueOrFalse = map[string]bool{"true": true, "false": false, "1": true, "0": false}
type ServiceResource struct {
//
}
func (s *ServiceResource) URLPatterns() []rest.Route {
return []rest.Route{
{Method: http.MethodGet, Path: "/v4/:project/registry/existence", Func: s.ResourceExist},
{Method: http.MethodGet, Path: "/v4/:project/registry/microservices", Func: s.ListService},
{Method: http.MethodGet, Path: "/v4/:project/registry/microservices/:serviceId", Func: s.GetService},
{Method: http.MethodPost, Path: "/v4/:project/registry/microservices", Func: s.RegisterService},
{Method: http.MethodPut, Path: "/v4/:project/registry/microservices/:serviceId/properties", Func: s.PutServiceProperties},
{Method: http.MethodDelete, Path: "/v4/:project/registry/microservices/:serviceId", Func: s.UnregisterService},
{Method: http.MethodDelete, Path: "/v4/:project/registry/microservices", Func: s.UnregisterManyService},
// tags
{Method: http.MethodPost, Path: "/v4/:project/registry/microservices/:serviceId/tags", Func: s.PutManyTags},
{Method: http.MethodPut, Path: "/v4/:project/registry/microservices/:serviceId/tags/:key", Func: s.PutTag},
{Method: http.MethodGet, Path: "/v4/:project/registry/microservices/:serviceId/tags", Func: s.ListTag},
{Method: http.MethodDelete, Path: "/v4/:project/registry/microservices/:serviceId/tags/:key", Func: s.DeleteManyTags},
}
}
func (s *ServiceResource) RegisterService(w http.ResponseWriter, r *http.Request) {
message, err := io.ReadAll(r.Body)
if err != nil {
log.Error("read body failed", err)
rest.WriteError(w, pb.ErrInvalidParams, err.Error())
return
}
var request pb.CreateServiceRequest
err = json.Unmarshal(message, &request)
if err != nil {
log.Error(fmt.Sprintf("invalid json: %s", util.BytesToStringWithNoCopy(message)), err)
rest.WriteError(w, pb.ErrInvalidParams, err.Error())
return
}
resp, err := discosvc.RegisterService(r.Context(), &request)
if err != nil {
log.Error("create service failed", err)
rest.WriteServiceError(w, err)
return
}
rest.WriteResponse(w, r, nil, resp)
}
func (s *ServiceResource) PutServiceProperties(w http.ResponseWriter, r *http.Request) {
message, err := io.ReadAll(r.Body)
if err != nil {
log.Error("read body failed", err)
rest.WriteError(w, pb.ErrInvalidParams, err.Error())
return
}
request := &pb.UpdateServicePropsRequest{
ServiceId: r.URL.Query().Get(":serviceId"),
}
err = json.Unmarshal(message, request)
if err != nil {
log.Error(fmt.Sprintf("invalid json: %s", util.BytesToStringWithNoCopy(message)), err)
rest.WriteError(w, pb.ErrInvalidParams, err.Error())
return
}
err = discosvc.PutServiceProperties(r.Context(), request)
if err != nil {
log.Error("can not update service", err)
rest.WriteServiceError(w, err)
return
}
rest.WriteResponse(w, r, nil, nil)
}
func (s *ServiceResource) UnregisterService(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
serviceID := query.Get(":serviceId")
force := query.Get("force")
b, ok := trueOrFalse[force]
if force != "" && !ok {
rest.WriteError(w, pb.ErrInvalidParams, "parameter force must be false or true")
return
}
request := &pb.DeleteServiceRequest{
ServiceId: serviceID,
Force: b,
}
err := discosvc.UnregisterService(r.Context(), request)
if err != nil {
log.Error(fmt.Sprintf("delete service[%s] failed", serviceID), err)
rest.WriteServiceError(w, err)
return
}
rest.WriteResponse(w, r, nil, nil)
}
func (s *ServiceResource) ListService(w http.ResponseWriter, r *http.Request) {
request := &pb.GetServicesRequest{
WithShared: util.StringTRUE(r.URL.Query().Get("withShared")),
}
resp, err := discosvc.ListService(r.Context(), request)
if err != nil {
log.Error("list services failed", err)
rest.WriteServiceError(w, err)
return
}
rest.WriteResponse(w, r, nil, resp)
}
func (s *ServiceResource) ResourceExist(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
query := r.URL.Query()
checkType := query.Get("type")
request := &pb.GetExistenceRequest{
Type: checkType,
Environment: query.Get("env"),
AppId: query.Get("appId"),
ServiceName: query.Get("serviceName"),
Version: query.Get("version"),
ServiceId: query.Get("serviceId"),
SchemaId: query.Get("schemaId"),
}
resp, err := resourceExist(ctx, w, request)
if err != nil {
log.Error("check resource existence failed", err)
rest.WriteServiceError(w, err)
return
}
rest.WriteResponse(w, r, nil, resp)
}
func resourceExist(ctx context.Context, w http.ResponseWriter, request *pb.GetExistenceRequest) (*pb.GetExistenceResponse, error) {
switch request.Type {
case datasource.ExistTypeMicroservice:
serviceID, err := discosvc.ExistService(ctx, request)
if err != nil {
return nil, err
}
return &pb.GetExistenceResponse{ServiceId: serviceID}, nil
case datasource.ExistTypeSchema:
schema, err := discosvc.ExistSchema(ctx, &pb.GetSchemaRequest{
ServiceId: request.ServiceId,
SchemaId: request.SchemaId,
})
if err != nil {
return nil, err
}
w.Header().Add("X-Schema-Summary", schema.Summary)
return &pb.GetExistenceResponse{
ServiceId: request.ServiceId,
SchemaId: schema.SchemaId,
}, nil
default:
log.Warn(fmt.Sprintf("unexpected type '%s' for existence query.", request.Type))
return nil, pb.NewError(pb.ErrInvalidParams, "Only micro-service and schema can be used as type.")
}
}
func (s *ServiceResource) GetService(w http.ResponseWriter, r *http.Request) {
request := &pb.GetServiceRequest{
ServiceId: r.URL.Query().Get(":serviceId"),
}
service, err := discosvc.GetService(r.Context(), request)
if err != nil {
log.Error(fmt.Sprintf("get service[%s] failed", request.ServiceId), err)
rest.WriteServiceError(w, err)
return
}
rest.WriteResponse(w, r, nil, &pb.GetServiceResponse{Service: service})
}
func (s *ServiceResource) UnregisterManyService(w http.ResponseWriter, r *http.Request) {
message, err := io.ReadAll(r.Body)
if err != nil {
log.Error("read body failed", err)
rest.WriteError(w, pb.ErrInvalidParams, err.Error())
return
}
request := &pb.DelServicesRequest{}
err = json.Unmarshal(message, request)
if err != nil {
log.Error(fmt.Sprintf("invalid json: %s", util.BytesToStringWithNoCopy(message)), err)
rest.WriteError(w, pb.ErrInvalidParams, err.Error())
return
}
resp, err := discosvc.UnregisterManyService(r.Context(), request)
if err != nil {
log.Error("delete services failed", err)
rest.WriteServiceError(w, err)
return
}
rest.WriteResponse(w, r, nil, resp)
}
func (s *ServiceResource) PutManyTags(w http.ResponseWriter, r *http.Request) {
message, err := io.ReadAll(r.Body)
if err != nil {
log.Error("read body failed", err)
rest.WriteError(w, pb.ErrInvalidParams, err.Error())
return
}
var tags map[string]map[string]string
err = json.Unmarshal(message, &tags)
if err != nil {
log.Error(fmt.Sprintf("invalid json: %s", util.BytesToStringWithNoCopy(message)), err)
rest.WriteError(w, pb.ErrInvalidParams, err.Error())
return
}
err = discosvc.PutManyTags(r.Context(), &pb.AddServiceTagsRequest{
ServiceId: r.URL.Query().Get(":serviceId"),
Tags: tags["tags"],
})
if err != nil {
log.Error("can not add tag", err)
rest.WriteServiceError(w, err)
return
}
rest.WriteResponse(w, r, nil, nil)
}
func (s *ServiceResource) PutTag(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
err := discosvc.PutTag(r.Context(), &pb.UpdateServiceTagRequest{
ServiceId: query.Get(":serviceId"),
Key: query.Get(":key"),
Value: query.Get("value"),
})
if err != nil {
log.Error("can not update tag", err)
rest.WriteServiceError(w, err)
return
}
rest.WriteResponse(w, r, nil, nil)
}
func (s *ServiceResource) ListTag(w http.ResponseWriter, r *http.Request) {
resp, err := discosvc.ListTag(r.Context(), &pb.GetServiceTagsRequest{
ServiceId: r.URL.Query().Get(":serviceId"),
})
if err != nil {
log.Error("can not list tag", err)
rest.WriteServiceError(w, err)
return
}
rest.WriteResponse(w, r, nil, resp)
}
func (s *ServiceResource) DeleteManyTags(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
keys := query.Get(":key")
ids := strings.Split(keys, ",")
err := discosvc.DeleteManyTags(r.Context(), &pb.DeleteServiceTagsRequest{
ServiceId: query.Get(":serviceId"),
Keys: ids,
})
if err != nil {
log.Error("can not delete many tag", err)
rest.WriteServiceError(w, err)
return
}
rest.WriteResponse(w, r, nil, nil)
}