diff --git a/cmd/apis/ghcrtapis.go b/cmd/apis/ghcrtapis.go index a02183704..f95faa6a1 100644 --- a/cmd/apis/ghcrtapis.go +++ b/cmd/apis/ghcrtapis.go @@ -48,12 +48,12 @@ var GhCreateCmd = &cobra.Command{ if os.Getenv("GITHUB_TOKEN") == "" { clilog.Debug.Println("github token is not set as an env var. Running unauthenticated") } - if err = proxybundle.GitHubImportBundle(ghOwner, ghRepo, ghPath); err != nil { - proxybundle.CleanUp() + if err = proxybundle.GitHubImportBundle(ghOwner, ghRepo, ghPath, false); err != nil { + proxybundle.ProxyCleanUp() return err } _, err = apis.CreateProxy(name, bundleName) - proxybundle.CleanUp() + proxybundle.ProxyCleanUp() return err }, } diff --git a/cmd/sharedflows/bundlecrtsf.go b/cmd/sharedflows/bundlecrtsf.go new file mode 100644 index 000000000..021b8b0b5 --- /dev/null +++ b/cmd/sharedflows/bundlecrtsf.go @@ -0,0 +1,93 @@ +// Copyright 2020 Google LLC +// +// 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 sharedflows + +import ( + "fmt" + "os" + "path" + "path/filepath" + + "internal/apiclient" + + "internal/bundlegen/proxybundle" + + "internal/client/sharedflows" + + "github.com/spf13/cobra" +) + +// BundleCreateCmd to create shared flow +var BundleCreateCmd = &cobra.Command{ + Use: "bundle", + Short: "Creates a sharedflow in an Apigee Org", + Long: "Creates a sharedflow in an Apigee Org", + Args: func(cmd *cobra.Command, args []string) (err error) { + apiclient.SetApigeeEnv(env) + if sfZip != "" && sfFolder != "" { + return fmt.Errorf("sharedflow bundle (zip) and folder to a sharedflow cannot be combined") + } + if sfZip == "" && sfFolder == "" { + return fmt.Errorf("either sharedflow bundle (zip) or folder must be specified, not both") + } + if sfFolder != "" { + if _, err := os.Stat(sfFolder); os.IsNotExist(err) { + return err + } + } + return apiclient.SetApigeeOrg(org) + }, + RunE: func(cmd *cobra.Command, args []string) (err error) { + if sfZip != "" { + _, err = sharedflows.Create(name, sfZip) + } else if sfFolder != "" { + if stat, err := os.Stat(folder); err == nil && !stat.IsDir() { + return fmt.Errorf("supplied path is not a folder") + } + if filepath.Base(sfFolder) != "sharedflowbundle" { + return fmt.Errorf("--sf-folder or -p must be a path to sharedflowbundle folder") + } + tmpDir, err := os.MkdirTemp("", "sf") + if err != nil { + return err + } + defer os.RemoveAll(tmpDir) + + sfBundlePath := path.Join(tmpDir, name+".zip") + + if err = proxybundle.GenerateArchiveBundle(sfFolder, sfBundlePath); err != nil { + return err + } + if _, err = sharedflows.Create(name, sfBundlePath); err != nil { + return err + } + return os.Remove(sfBundlePath) + } + return err + }, +} + +var sfZip, sfFolder string + +func init() { + BundleCreateCmd.Flags().StringVarP(&name, "name", "n", + "", "Sharedflow name") + BundleCreateCmd.Flags().StringVarP(&sfZip, "sf-zip", "p", + "", "Path to the Sharedflow bundle/zip file") + BundleCreateCmd.Flags().StringVarP(&sfFolder, "sf-folder", "f", + "", "Path to the Sharedflow Bundle; ex: ./test/sharedflowbundle") + + _ = BundleCreateCmd.MarkFlagRequired("name") +} diff --git a/cmd/sharedflows/crtsf.go b/cmd/sharedflows/crtsf.go index 0a60acefe..3f13e179b 100644 --- a/cmd/sharedflows/crtsf.go +++ b/cmd/sharedflows/crtsf.go @@ -15,79 +15,17 @@ package sharedflows import ( - "fmt" - "os" - "path" - "path/filepath" - - "internal/apiclient" - - "internal/bundlegen/proxybundle" - - "internal/client/sharedflows" - "github.com/spf13/cobra" ) -// CreateCmd to create shared flow +// CreateCmd to create sharedflow var CreateCmd = &cobra.Command{ Use: "create", - Short: "Creates a sharedflow in an Apigee Org", - Long: "Creates a sharedflow in an Apigee Org", - Args: func(cmd *cobra.Command, args []string) (err error) { - apiclient.SetApigeeEnv(env) - if sfZip != "" && sfFolder != "" { - return fmt.Errorf("sharedflow bundle (zip) and folder to a sharedflow cannot be combined") - } - if sfZip == "" && sfFolder == "" { - return fmt.Errorf("either sharedflow bundle (zip) or folder must be specified, not both") - } - if sfFolder != "" { - if _, err := os.Stat(sfFolder); os.IsNotExist(err) { - return err - } - } - return apiclient.SetApigeeOrg(org) - }, - RunE: func(cmd *cobra.Command, args []string) (err error) { - if sfZip != "" { - _, err = sharedflows.Create(name, sfZip) - } else if sfFolder != "" { - if stat, err := os.Stat(folder); err == nil && !stat.IsDir() { - return fmt.Errorf("supplied path is not a folder") - } - if filepath.Base(sfFolder) != "sharedflowbundle" { - return fmt.Errorf("--sf-folder or -p must be a path to sharedflowbundle folder") - } - tmpDir, err := os.MkdirTemp("", "sf") - if err != nil { - return err - } - defer os.RemoveAll(tmpDir) - - sfBundlePath := path.Join(tmpDir, name+".zip") - - if err = proxybundle.GenerateArchiveBundle(sfFolder, sfBundlePath); err != nil { - return err - } - if _, err = sharedflows.Create(name, sfBundlePath); err != nil { - return err - } - return os.Remove(sfBundlePath) - } - return err - }, + Short: "Creates a Sharedflow in an Apigee Org", + Long: "Creates a Sharedflow in an Apigee Org", } -var sfZip, sfFolder string - func init() { - CreateCmd.Flags().StringVarP(&name, "name", "n", - "", "Sharedflow name") - CreateCmd.Flags().StringVarP(&sfZip, "sf-zip", "p", - "", "Path to the Sharedflow bundle/zip file") - CreateCmd.Flags().StringVarP(&sfFolder, "sf-folder", "f", - "", "Path to the Sharedflow Bundle; ex: ./test/sharedflowbundle") - - _ = CreateCmd.MarkFlagRequired("name") + CreateCmd.AddCommand(GhCreateCmd) + CreateCmd.AddCommand(BundleCreateCmd) } diff --git a/cmd/sharedflows/ghcrtsf.go b/cmd/sharedflows/ghcrtsf.go new file mode 100644 index 000000000..e130cecbd --- /dev/null +++ b/cmd/sharedflows/ghcrtsf.go @@ -0,0 +1,79 @@ +// Copyright 2021 Google LLC +// +// 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 sharedflows + +import ( + "fmt" + "os" + "regexp" + + "internal/apiclient" + "internal/client/sharedflows" + + "internal/clilog" + + proxybundle "internal/bundlegen/proxybundle" + + "github.com/spf13/cobra" +) + +// GhCreateCmd create an api from a github repo +var GhCreateCmd = &cobra.Command{ + Use: "github", + Aliases: []string{"gh"}, + Short: "Creates a sharedflow from a GitHub repo", + Long: "Creates a sharedflow from a GitHub repo", + Args: func(cmd *cobra.Command, args []string) (err error) { + re := regexp.MustCompile(`(\w+)?\/sharedflowbundle$`) + if ok := re.Match([]byte(ghPath)); !ok { + return fmt.Errorf("github path must end with /sharedflowbundle") + } + + return apiclient.SetApigeeOrg(org) + }, + RunE: func(cmd *cobra.Command, args []string) (err error) { + if os.Getenv("GITHUB_TOKEN") == "" { + clilog.Debug.Println("github token is not set as an env var. Running unauthenticated") + } + if err = proxybundle.GitHubImportBundle(ghOwner, ghRepo, ghPath, true); err != nil { + proxybundle.SharedflowCleanUp() + return err + } + + _, err = sharedflows.Create(name, bundleName) + proxybundle.SharedflowCleanUp() + return err + }, +} + +const bundleName = "sharedflowbundle.zip" + +var ghOwner, ghRepo, ghPath string + +func init() { + GhCreateCmd.Flags().StringVarP(&name, "name", "n", + "", "Sharedflow name") + GhCreateCmd.Flags().StringVarP(&ghOwner, "owner", "u", + "", "The github organization or username. ex: In https://github.com/apigee, apigee is the owner name") + GhCreateCmd.Flags().StringVarP(&ghRepo, "repo", "r", + "", "The github repo name. ex: https://github.com/apigee/api-platform-samples, api-platform-samples is the repo") + GhCreateCmd.Flags().StringVarP(&ghPath, "sf-path", "p", + "", "The path in the repo to the sharedflowbundle folder. ex: sample-proxies/security/sharedflowbundle") + + _ = GhCreateCmd.MarkFlagRequired("name") + _ = GhCreateCmd.MarkFlagRequired("owner") + _ = GhCreateCmd.MarkFlagRequired("repo") + _ = GhCreateCmd.MarkFlagRequired("sf-path") +} diff --git a/internal/bundlegen/proxybundle/proxybundle.go b/internal/bundlegen/proxybundle/proxybundle.go index 49e2a831c..9f1c87536 100644 --- a/internal/bundlegen/proxybundle/proxybundle.go +++ b/internal/bundlegen/proxybundle/proxybundle.go @@ -43,7 +43,8 @@ import ( "golang.org/x/oauth2" ) -var rootDir = "apiproxy" +var proxyRootDir = "apiproxy" +var sfRootDir = "sharedflowbundle" func GenerateAPIProxyBundleFromOAS(name string, content string, @@ -60,9 +61,9 @@ func GenerateAPIProxyBundleFromOAS(name string, return err } - rootDir = path.Join(tmpDir, rootDir) + proxyRootDir = path.Join(tmpDir, proxyRootDir) - if err = os.Mkdir(rootDir, os.ModePerm); err != nil { + if err = os.Mkdir(proxyRootDir, os.ModePerm); err != nil { return err } @@ -71,16 +72,16 @@ func GenerateAPIProxyBundleFromOAS(name string, return err } - err = writeXMLData(rootDir+string(os.PathSeparator)+name+".xml", apiProxyData) + err = writeXMLData(proxyRootDir+string(os.PathSeparator)+name+".xml", apiProxyData) if err != nil { return err } - proxiesDirPath := rootDir + string(os.PathSeparator) + "proxies" - policiesDirPath := rootDir + string(os.PathSeparator) + "policies" - targetDirPath := rootDir + string(os.PathSeparator) + "targets" - resDirPath := rootDir + string(os.PathSeparator) + "resources" + string(os.PathSeparator) + resourceType //"oas" - integrationDirPath := rootDir + string(os.PathSeparator) + "integration-endpoints" + proxiesDirPath := proxyRootDir + string(os.PathSeparator) + "proxies" + policiesDirPath := proxyRootDir + string(os.PathSeparator) + "policies" + targetDirPath := proxyRootDir + string(os.PathSeparator) + "targets" + resDirPath := proxyRootDir + string(os.PathSeparator) + "resources" + string(os.PathSeparator) + resourceType //"oas" + integrationDirPath := proxyRootDir + string(os.PathSeparator) + "integration-endpoints" if err = os.Mkdir(proxiesDirPath, os.ModePerm); err != nil { return err @@ -203,11 +204,11 @@ func GenerateAPIProxyBundleFromOAS(name string, } } - if err = archiveBundle(rootDir, name+".zip"); err != nil { + if err = archiveBundle(proxyRootDir, name+".zip"); err != nil { return err } - defer os.RemoveAll(rootDir) // clean up + defer os.RemoveAll(proxyRootDir) // clean up return nil } @@ -230,9 +231,9 @@ func GenerateAPIProxyBundleFromGQL(name string, return err } - rootDir = path.Join(tmpDir, rootDir) + proxyRootDir = path.Join(tmpDir, proxyRootDir) - if err = os.Mkdir(rootDir, os.ModePerm); err != nil { + if err = os.Mkdir(proxyRootDir, os.ModePerm); err != nil { return err } @@ -241,15 +242,15 @@ func GenerateAPIProxyBundleFromGQL(name string, return err } - err = writeXMLData(rootDir+string(os.PathSeparator)+name+".xml", apiProxyData) + err = writeXMLData(proxyRootDir+string(os.PathSeparator)+name+".xml", apiProxyData) if err != nil { return err } - proxiesDirPath := rootDir + string(os.PathSeparator) + "proxies" - policiesDirPath := rootDir + string(os.PathSeparator) + "policies" - targetDirPath := rootDir + string(os.PathSeparator) + "targets" - resDirPath := rootDir + string(os.PathSeparator) + "resources" + string(os.PathSeparator) + resourceType //"graphql" + proxiesDirPath := proxyRootDir + string(os.PathSeparator) + "proxies" + policiesDirPath := proxyRootDir + string(os.PathSeparator) + "policies" + targetDirPath := proxyRootDir + string(os.PathSeparator) + "targets" + resDirPath := proxyRootDir + string(os.PathSeparator) + "resources" + string(os.PathSeparator) + resourceType //"graphql" if err = os.Mkdir(proxiesDirPath, os.ModePerm); err != nil { return err @@ -321,11 +322,11 @@ func GenerateAPIProxyBundleFromGQL(name string, } } - if err = archiveBundle(rootDir, name+".zip"); err != nil { + if err = archiveBundle(proxyRootDir, name+".zip"); err != nil { return err } - defer os.RemoveAll(rootDir) // clean up + defer os.RemoveAll(proxyRootDir) // clean up return nil } @@ -337,9 +338,9 @@ func GenerateIntegrationAPIProxyBundle(name string, integration string, apitrigg return err } - rootDir = path.Join(tmpDir, rootDir) + proxyRootDir = path.Join(tmpDir, proxyRootDir) - if err = os.Mkdir(rootDir, os.ModePerm); err != nil { + if err = os.Mkdir(proxyRootDir, os.ModePerm); err != nil { return err } @@ -348,14 +349,14 @@ func GenerateIntegrationAPIProxyBundle(name string, integration string, apitrigg return err } - err = writeXMLData(rootDir+string(os.PathSeparator)+name+".xml", apiProxyData) + err = writeXMLData(proxyRootDir+string(os.PathSeparator)+name+".xml", apiProxyData) if err != nil { return err } - proxiesDirPath := rootDir + string(os.PathSeparator) + "proxies" - policiesDirPath := rootDir + string(os.PathSeparator) + "policies" - integrationDirPath := rootDir + string(os.PathSeparator) + "integration-endpoints" + proxiesDirPath := proxyRootDir + string(os.PathSeparator) + "proxies" + policiesDirPath := proxyRootDir + string(os.PathSeparator) + "policies" + integrationDirPath := proxyRootDir + string(os.PathSeparator) + "integration-endpoints" if err = os.Mkdir(proxiesDirPath, os.ModePerm); err != nil { return err @@ -389,11 +390,11 @@ func GenerateIntegrationAPIProxyBundle(name string, integration string, apitrigg return err } - if err = archiveBundle(rootDir, name+".zip"); err != nil { + if err = archiveBundle(proxyRootDir, name+".zip"); err != nil { return err } - defer os.RemoveAll(rootDir) // clean up + defer os.RemoveAll(proxyRootDir) // clean up return nil } @@ -408,13 +409,13 @@ func GenerateAPIProxyBundleFromSwagger(name string, return err } - rootDir = path.Join(tmpDir, rootDir) + proxyRootDir = path.Join(tmpDir, proxyRootDir) if name == "" { name = bundlegen.GetGoogleApiName() } - if err = os.Mkdir(rootDir, os.ModePerm); err != nil { + if err = os.Mkdir(proxyRootDir, os.ModePerm); err != nil { return err } @@ -423,14 +424,14 @@ func GenerateAPIProxyBundleFromSwagger(name string, return err } - err = writeXMLData(rootDir+string(os.PathSeparator)+name+".xml", apiProxyData) + err = writeXMLData(proxyRootDir+string(os.PathSeparator)+name+".xml", apiProxyData) if err != nil { return err } - proxiesDirPath := rootDir + string(os.PathSeparator) + "proxies" - policiesDirPath := rootDir + string(os.PathSeparator) + "policies" - targetDirPath := rootDir + string(os.PathSeparator) + "targets" + proxiesDirPath := proxyRootDir + string(os.PathSeparator) + "proxies" + policiesDirPath := proxyRootDir + string(os.PathSeparator) + "policies" + targetDirPath := proxyRootDir + string(os.PathSeparator) + "targets" if err = os.Mkdir(proxiesDirPath, os.ModePerm); err != nil { return err @@ -542,11 +543,11 @@ func GenerateAPIProxyBundleFromSwagger(name string, } } - if err = archiveBundle(rootDir, name+".zip"); err != nil { + if err = archiveBundle(proxyRootDir, name+".zip"); err != nil { return err } - defer os.RemoveAll(rootDir) // clean up + defer os.RemoveAll(proxyRootDir) // clean up return err } @@ -621,9 +622,22 @@ func archiveBundle(pathToZip, destinationPath string) (err error) { return nil } -func GitHubImportBundle(owner string, repo string, repopath string) (err error) { +func GitHubImportBundle(owner string, repo string, repopath string, sharedflow bool) (err error) { + + var rootDir string + + if sharedflow { + rootDir = sfRootDir + } else { + rootDir = proxyRootDir + } + // clean up any files or folders - CleanUp() + if sharedflow { + SharedflowCleanUp() + } + ProxyCleanUp() + os.RemoveAll(rootDir) var client *github.Client @@ -650,14 +664,10 @@ func GitHubImportBundle(owner string, repo string, repopath string) (err error) } // 1. download the proxy - if err := downloadProxyFromRepo(client, ctx, owner, repo, repopath); err != nil { + if err := downloadProxyFromRepo(client, ctx, owner, repo, repopath, sharedflow); err != nil { return err } - if client != nil { - clilog.Info.Println("") - } - // 2. compress the proxy folder curDir, _ := os.Getwd() if err := archiveBundle(path.Join(curDir, rootDir), path.Join(curDir, rootDir+".zip")); err != nil { @@ -668,13 +678,19 @@ func GitHubImportBundle(owner string, repo string, repopath string) (err error) return err } -func CleanUp() { - if _, err := os.Stat(rootDir + ".zip"); err == nil { - _ = os.Remove(rootDir + ".zip") +func ProxyCleanUp() { + if _, err := os.Stat(proxyRootDir + ".zip"); err == nil { + _ = os.Remove(proxyRootDir + ".zip") + } +} + +func SharedflowCleanUp() { + if _, err := os.Stat(sfRootDir + ".zip"); err == nil { + _ = os.Remove(sfRootDir + ".zip") } } -func downloadProxyFromRepo(client *github.Client, ctx context.Context, owner string, repo string, repopath string) (err error) { +func downloadProxyFromRepo(client *github.Client, ctx context.Context, owner string, repo string, repopath string, sharedflow bool) (err error) { var fileContent *github.RepositoryContent var directoryContents []*github.RepositoryContent @@ -683,7 +699,7 @@ func downloadProxyFromRepo(client *github.Client, ctx context.Context, owner str } if fileContent != nil { - if err = downloadResource(*fileContent.Path, *fileContent.DownloadURL); err != nil { + if err = downloadResource(*fileContent.Path, *fileContent.DownloadURL, sharedflow); err != nil { return err } } @@ -691,11 +707,11 @@ func downloadProxyFromRepo(client *github.Client, ctx context.Context, owner str if len(directoryContents) > 0 { for _, directoryContent := range directoryContents { if *directoryContent.Type == "dir" { - if err = downloadProxyFromRepo(client, ctx, owner, repo, path.Join(repopath, *directoryContent.Name)); err != nil { + if err = downloadProxyFromRepo(client, ctx, owner, repo, path.Join(repopath, *directoryContent.Name), sharedflow); err != nil { return err } } else if *directoryContent.Type == "file" { - if err = downloadResource(*directoryContent.Path, *directoryContent.DownloadURL); err != nil { + if err = downloadResource(*directoryContent.Path, *directoryContent.DownloadURL, sharedflow); err != nil { return err } } @@ -707,18 +723,34 @@ func downloadProxyFromRepo(client *github.Client, ctx context.Context, owner str func getApiProxyFolder(repoPath string) (apiProxyFolder string, apiProxyFile string) { re := regexp.MustCompile(`(\S*)?(\/?)apiproxy`) - apiProxyFileBytes := re.ReplaceAll([]byte(repoPath), []byte(rootDir)) + apiProxyFileBytes := re.ReplaceAll([]byte(repoPath), []byte(proxyRootDir)) apiProxyFile = string(apiProxyFileBytes) apiProxyFolder = filepath.Dir(apiProxyFile) return apiProxyFolder, apiProxyFile } +func getSharedflowFolder(repoPath string) (sfFolder string, sfFile string) { + re := regexp.MustCompile(`(\S*)?(\/?)sharedflowbundle`) + + sfFileBytes := re.ReplaceAll([]byte(repoPath), []byte(sfRootDir)) + sfFile = string(sfFileBytes) + + sfFolder = filepath.Dir(sfFile) + return sfFolder, sfFile +} + // downloadResource method is used to download resources, proxy bundles, sharedflows -func downloadResource(repoPath string, url string) (err error) { +func downloadResource(repoPath string, url string, sharedflow bool) (err error) { var apiproxyFolder, apiproxyFile string - if apiproxyFolder, apiproxyFile = getApiProxyFolder(repoPath); err != nil { + if sharedflow { + if apiproxyFolder, apiproxyFile = getSharedflowFolder(repoPath); err != nil { + return err + } + } + + if apiproxyFolder, apiproxyFile = getSharedflowFolder(repoPath); err != nil { return err }