From ec2450c4bda9edb00ef5e55599c708b7f5f491dc Mon Sep 17 00:00:00 2001 From: Tim Burks Date: Thu, 9 Mar 2023 15:20:26 -0800 Subject: [PATCH] first attempt to export from API Gateway. --- .../discover/apigateway/apigateway.go | 29 ++ .../discover/apigateway/gateways/gateways.go | 268 ++++++++++++++++++ cmd/registry-connect/discover/discover.go | 2 + 3 files changed, 299 insertions(+) create mode 100644 cmd/registry-connect/discover/apigateway/apigateway.go create mode 100644 cmd/registry-connect/discover/apigateway/gateways/gateways.go diff --git a/cmd/registry-connect/discover/apigateway/apigateway.go b/cmd/registry-connect/discover/apigateway/apigateway.go new file mode 100644 index 00000000..bd3ca382 --- /dev/null +++ b/cmd/registry-connect/discover/apigateway/apigateway.go @@ -0,0 +1,29 @@ +// Copyright 2023 Google LLC. All Rights Reserved. +// +// 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 apigateway + +import ( + "github.com/apigee/registry-experimental/cmd/registry-connect/discover/apigateway/gateways" + "github.com/spf13/cobra" +) + +func Command() *cobra.Command { + var cmd = &cobra.Command{ + Use: "apigateway", + Short: "Registry commands relating to API Gateway", + } + cmd.AddCommand(gateways.Command()) + return cmd +} diff --git a/cmd/registry-connect/discover/apigateway/gateways/gateways.go b/cmd/registry-connect/discover/apigateway/gateways/gateways.go new file mode 100644 index 00000000..45c48439 --- /dev/null +++ b/cmd/registry-connect/discover/apigateway/gateways/gateways.go @@ -0,0 +1,268 @@ +// Copyright 2023 Google LLC. All Rights Reserved. +// +// 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 gateways + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/apigee/registry/pkg/encoding" + "github.com/spf13/cobra" +) + +const weNeedToReadAPIs = false + +func Command() *cobra.Command { + var output string + var cmd = &cobra.Command{ + Use: "gateways", + Short: "Export API Gateway Gateways", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) (err error) { + project, err := getProject() + if err != nil { + return err + } + if weNeedToReadAPIs { + apis, err := fetchApis() + if err != nil { + return err + } + for i, api := range apis { + fmt.Printf("%d %+v\n", i, api) + } + } + gateways, err := fetchGateways() + if err != nil { + return err + } + for _, gateway := range gateways { + fmt.Printf("exporting %s\n", gateway.Name) + config, err := fetchConfig(gateway.ApiConfig) + if err != nil { + return err + } + err = writeRegistryYAML(output, project, &gateway, config) + if err != nil { + return err + } + } + return nil + }, + } + cmd.Flags().StringVar(&output, "output", "gateway-export", "output directory") + + return cmd +} + +func getProject() (string, error) { + buf := &bytes.Buffer{} + command := "gcloud config get project" + cmdargs := strings.Split(command, " ") + c := exec.Command(cmdargs[0], cmdargs[1:]...) + c.Stdout = buf + if err := c.Run(); err != nil { + return "", err + } + return strings.TrimSpace(buf.String()), nil +} + +func fetchApis() ([]GatewayApi, error) { + buf := &bytes.Buffer{} + command := "gcloud api-gateway apis list --format json" + cmdargs := strings.Split(command, " ") + c := exec.Command(cmdargs[0], cmdargs[1:]...) + c.Stdout = buf + if err := c.Run(); err != nil { + return nil, err + } + var apis []GatewayApi + json.Unmarshal(buf.Bytes(), &apis) + return apis, nil +} + +func fetchGateways() ([]Gateway, error) { + buf := &bytes.Buffer{} + command := "gcloud api-gateway gateways list --format json" + cmdargs := strings.Split(command, " ") + c := exec.Command(cmdargs[0], cmdargs[1:]...) + c.Stdout = buf + if err := c.Run(); err != nil { + return nil, err + } + var gateways []Gateway + json.Unmarshal(buf.Bytes(), &gateways) + return gateways, nil +} + +func fetchConfig(name string) (*GatewayConfig, error) { + buf := &bytes.Buffer{} + command := "gcloud api-gateway api-configs describe --view FULL --format json " + name + cmdargs := strings.Split(command, " ") + c := exec.Command(cmdargs[0], cmdargs[1:]...) + c.Stdout = buf + if err := c.Run(); err != nil { + return nil, err + } + var config GatewayConfig + json.Unmarshal(buf.Bytes(), &config) + return &config, nil +} + +type GatewayApi struct { + CreateTime string `json:"createTime"` + DisplayName string `json:"displayName"` + ManagedService string `json:"managedService"` + Name string `json:"name"` + State string `json:"state"` + UpdateTime string `json:"updateTime"` +} + +type Gateway struct { + ApiConfig string `json:"apiConfig"` + CreateTime string `json:"createTime"` + DefaultHostname string `json:"defaultHostname"` + DisplayName string `json:"displayName"` + Name string `json:"name"` + State string `json:"state"` + UpdateTime string `json:"updateTime"` +} + +type GatewayConfig struct { + CreateTime string `json:"createTime"` + DisplayName string `json:"displayName"` + GatewayServiceAccount string `json:"gatewayServiceAccount"` + GrpcServices []map[string][]GatewayFile `json:"grpcServices"` + ManagedServiceConfigs []*GatewayFile `json:"managedServiceConfigs"` + OpenAPIDocuments []map[string]GatewayFile `json:"openapiDocuments"` + Name string `json:"name"` + ServiceConfigId string `json:"serviceConfigId"` + State string `json:"state"` + UpdateTime string `json:"updateTime"` +} + +type GatewayFile struct { + Contents string `json:"contents"` + Path string `json:"path"` +} + +func writeRegistryYAML(output, project string, gateway *Gateway, config *GatewayConfig) error { + apiId := filepath.Base(gateway.Name) + err := os.MkdirAll(filepath.Join(output, project+"-"+apiId), 0777) + if err != nil { + return err + } + description := "Exported from API Gateway" + + specname := "openapi" + filename := "openapi.yaml" + mimetype := "application/x.openapi+gzip;version=2.0" + if config.GrpcServices != nil { + specname = "protos" + filename = "protos.zip" + mimetype = "application/x.proto+zip" + } + a := &encoding.Api{ + Header: encoding.Header{ + ApiVersion: "apigeeregistry/v1", + Kind: "API", + Metadata: encoding.Metadata{ + Name: project + "-" + apiId, + Labels: map[string]string{ + "provider": strings.ReplaceAll(project, ".", "-"), + }, + }, + }, + Data: encoding.ApiData{ + DisplayName: project + " gateway " + apiId, + Description: description, + ApiVersions: []*encoding.ApiVersion{ + { + Header: encoding.Header{ + Metadata: encoding.Metadata{ + Name: "v1", + }, + }, + Data: encoding.ApiVersionData{ + DisplayName: "v1", + ApiSpecs: []*encoding.ApiSpec{ + { + Header: encoding.Header{ + Metadata: encoding.Metadata{ + Name: specname, + }, + }, + Data: encoding.ApiSpecData{ + FileName: filename, + MimeType: mimetype, + }, + }, + }, + }, + }, + }, + ApiDeployments: []*encoding.ApiDeployment{ + { + Header: encoding.Header{ + Metadata: encoding.Metadata{ + Name: "gateway", + }, + }, + Data: encoding.ApiDeploymentData{ + DisplayName: "gateway", + EndpointURI: "https://" + gateway.DefaultHostname, + }, + }, + }, + }, + } + b, err := encoding.EncodeYAML(a) + if err != nil { + panic(err) + } + os.WriteFile(filepath.Join(output, project+"-"+apiId, "info.yaml"), b, 0666) + for _, openapi := range config.OpenAPIDocuments { + f := openapi["document"] + contents, err := base64.StdEncoding.DecodeString(f.Contents) + if err != nil { + return err + } + os.WriteFile(filepath.Join(output, project+"-"+apiId, f.Path), contents, 0666) + } + for _, grpcService := range config.GrpcServices { + files := grpcService["source"] + for _, f := range files { + contents, err := base64.StdEncoding.DecodeString(f.Contents) + if err != nil { + return err + } + os.WriteFile(filepath.Join(output, project+"-"+apiId, f.Path), contents, 0666) + } + } + for _, f := range config.ManagedServiceConfigs { + contents, err := base64.StdEncoding.DecodeString(f.Contents) + if err != nil { + return err + } + os.WriteFile(filepath.Join(output, project+"-"+apiId, f.Path), contents, 0666) + } + return nil +} diff --git a/cmd/registry-connect/discover/discover.go b/cmd/registry-connect/discover/discover.go index d746d038..36c679ad 100644 --- a/cmd/registry-connect/discover/discover.go +++ b/cmd/registry-connect/discover/discover.go @@ -15,6 +15,7 @@ package discover import ( + "github.com/apigee/registry-experimental/cmd/registry-connect/discover/apigateway" "github.com/apigee/registry-experimental/cmd/registry-connect/discover/apigee" "github.com/spf13/cobra" ) @@ -26,5 +27,6 @@ func Command() *cobra.Command { Args: cobra.NoArgs, } cmd.AddCommand(apigee.Command()) + cmd.AddCommand(apigateway.Command()) return cmd }