Skip to content

Commit

Permalink
feat: support export of sec profiles
Browse files Browse the repository at this point in the history
  • Loading branch information
srinandan committed Dec 1, 2023
1 parent c90226c commit 446f606
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 5 deletions.
58 changes: 58 additions & 0 deletions cmd/securityprofiles/export.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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 securityprofiles

import (

Check failure on line 17 in cmd/securityprofiles/export.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gofumpt`-ed (gofumpt)

Check failure on line 17 in cmd/securityprofiles/export.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gofumpt`-ed (gofumpt)
"internal/apiclient"
"os"

Check failure on line 19 in cmd/securityprofiles/export.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gofumpt`-ed (gofumpt)

Check failure on line 19 in cmd/securityprofiles/export.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gofumpt`-ed (gofumpt)

"internal/client/securityprofiles"

"github.com/spf13/cobra"
)

// ExpCmd to export sec profiles
var ExpCmd = &cobra.Command{
Use: "export",
Short: "Export Security Profiles to a file",
Long: "Export Security Profiles to a file",
Args: func(cmd *cobra.Command, args []string) (err error) {
return apiclient.SetApigeeOrg(org)
},
RunE: func(cmd *cobra.Command, args []string) (err error) {
if folder == "" {
folder, _ = os.Getwd()
}
if err = apiclient.FolderExists(folder); err != nil {
return err
}
return securityprofiles.Export(conn, folder, allRevisions)
},
}

var (
conn int
folder string
allRevisions bool
)

func init() {
ExpCmd.Flags().IntVarP(&conn, "conn", "c",
4, "Number of connections")
ExpCmd.Flags().StringVarP(&folder, "folder", "f",
"", "folder to export API proxy bundles")
ExpCmd.Flags().BoolVarP(&allRevisions, "all", "",
false, "Export all proxy revisions")
}
4 changes: 3 additions & 1 deletion cmd/securityprofiles/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ var GetCmd = &cobra.Command{
return apiclient.SetApigeeOrg(org)
},
RunE: func(cmd *cobra.Command, args []string) (err error) {
_, err = securityprofiles.Get(name)
_, err = securityprofiles.Get(name, revision)
return
},
}
Expand All @@ -40,5 +40,7 @@ var name string
func init() {
GetCmd.Flags().StringVarP(&name, "name", "n",
"", "Name of the security profile")
GetCmd.Flags().StringVarP(&revision, "revision", "r",
"", "Revision of the security profile")
_ = GetCmd.MarkFlagRequired("name")
}
2 changes: 2 additions & 0 deletions cmd/securityprofiles/securityprofiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ func init() {
Cmd.AddCommand(DetachCmd)
Cmd.AddCommand(CreateCmd)
Cmd.AddCommand(ListRevisionsCmd)
Cmd.AddCommand(UpdateCmd)
Cmd.AddCommand(ExpCmd)

_ = Cmd.MarkFlagRequired("org")
}
50 changes: 50 additions & 0 deletions cmd/securityprofiles/update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2023 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 securityprofiles

import (
"internal/apiclient"
"internal/client/securityprofiles"

"github.com/apigee/apigeecli/cmd/utils"
"github.com/spf13/cobra"
)

// UpdateCmd to update a security profile
var UpdateCmd = &cobra.Command{
Use: "update",
Short: "Update an existing Security Profile",
Long: "Update an existing Security Profile",
Args: func(cmd *cobra.Command, args []string) (err error) {
return apiclient.SetApigeeOrg(org)
},
RunE: func(cmd *cobra.Command, args []string) (err error) {
content, err := utils.ReadFile(securityActionFile)
if err != nil {
return err
}
_, err = securityprofiles.Update(name, content)
return
},
}

func init() {
UpdateCmd.Flags().StringVarP(&name, "name", "n",
"", "Name of the security profile")
UpdateCmd.Flags().StringVarP(&securityActionFile, "file", "f",
"", "Path to a file containing Security Profile content")
_ = UpdateCmd.MarkFlagRequired("name")
_ = UpdateCmd.MarkFlagRequired("file")
}
149 changes: 145 additions & 4 deletions internal/client/securityprofiles/securityprofiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,30 @@ package securityprofiles

import (
"encoding/json"
"errors"
"fmt"
"net/url"
"path"
"strconv"
"strings"
"sync"

"internal/apiclient"
"internal/clilog"
)

type secprofiles struct {
SecurityProfiles []secprofile `json:"securityProfiles,omitempty"`
NextPageToken string `json:"nextPageToken,omitempty"`
}

type secprofile struct {
Name string `json:"name"`
DisplayName string `json:"displayName"`
Description string `json:"description,omitempty"`
RevisionID string `json:"revisionId,omitempty"`
ProfileConfig profileConfig `json:"profileConfig"`
ScoreConfigs []scoreConfig `json:"scoreConfigs,omitempty"`
ScoreConfigs []scoreConfig `json:"scoringConfigs,omitempty"`
}

type profileConfig struct {
Expand All @@ -51,6 +61,8 @@ type category struct {
Cors interface{} `json:"cors,omitempty"`
}

const maxPageSize = 50

// Create
func Create(name string, content []byte) (respBody []byte, err error) {
sc := secprofile{}
Expand Down Expand Up @@ -96,14 +108,18 @@ func Delete(name string) (respBody []byte, err error) {
}

// Get
func Get(name string) (respBody []byte, err error) {
func Get(name string, revision string) (respBody []byte, err error) {
u, _ := url.Parse(apiclient.BaseURL)
u.Path = path.Join(u.Path, apiclient.GetApigeeOrg(), "securityProfiles", name)
if revision != "" {
u.Path = path.Join(u.Path, apiclient.GetApigeeOrg(), "securityProfiles", name+"@"+revision)
} else {
u.Path = path.Join(u.Path, apiclient.GetApigeeOrg(), "securityProfiles", name)
}
respBody, err = apiclient.HttpClient(u.String())
return respBody, err
}

// ListVersions
// ListRevisions
func ListRevisions(name string) (respBody []byte, err error) {
u, _ := url.Parse(apiclient.BaseURL)
u.Path = path.Join(u.Path, apiclient.GetApigeeOrg(), "securityProfiles", name+":listRevisions")
Expand All @@ -127,3 +143,128 @@ func List(pageSize int, pageToken string) (respBody []byte, err error) {
respBody, err = apiclient.HttpClient(u.String())
return respBody, err
}

// Update
func Update(name string, content []byte) (respBody []byte, err error) {
sc := secprofile{}
if err = json.Unmarshal(content, &sc); err != nil {
return nil, err
}
u, _ := url.Parse(apiclient.BaseURL)
u.Path = path.Join(u.Path, apiclient.GetApigeeOrg(), "securityProfiles", name)
q := u.Query()
q.Set("updateMask", "description,profileConfig")
u.RawQuery = q.Encode()

respBody, err = apiclient.HttpClient(u.String(), string(content), "PATCH")
return respBody, err
}

// Export
func Export(conn int, folder string, allRevisions bool) (err error) {
apiclient.ClientPrintHttpResponse.Set(false)
defer apiclient.ClientPrintHttpResponse.Set(apiclient.GetCmdPrintHttpResponseSetting())

pageToken := ""
listsecprofiles := secprofiles{}

for {
l := secprofiles{}
listRespBytes, err := List(maxPageSize, pageToken)
if err != nil {
return fmt.Errorf("failed to fetch security profiles: %w", err)
}
err = json.Unmarshal(listRespBytes, &l)
if err != nil {
return fmt.Errorf("failed to unmarshall: %w", err)
}
listsecprofiles.SecurityProfiles = append(listsecprofiles.SecurityProfiles, l.SecurityProfiles...)
if l.NextPageToken == "" {
break
}
}

errChan := make(chan error)
workChan := make(chan secprofile, len(listsecprofiles.SecurityProfiles))

fanOutWg := sync.WaitGroup{}
fanInWg := sync.WaitGroup{}

errs := []string{}
fanInWg.Add(1)

go func() {
defer fanInWg.Done()
for {
newErr, ok := <-errChan
if !ok {
return
}
errs = append(errs, newErr.Error())
}
}()

for i := 0; i < conn; i++ {
fanOutWg.Add(1)
go exportWorker(&fanOutWg, workChan, folder, allRevisions, errChan)
}

for _, i := range listsecprofiles.SecurityProfiles {
workChan <- i
}

close(workChan)
fanOutWg.Wait()
close(errChan)
fanInWg.Wait()

if len(errs) > 0 {
return errors.New(strings.Join(errs, "\n"))
}

return nil
}

func exportWorker(wg *sync.WaitGroup, workCh <-chan secprofile, folder string, allRevisions bool, errs chan<- error) {
defer wg.Done()
for {
var respBody []byte
var err error

listsecprofiles := secprofiles{}

work, ok := <-workCh
if !ok {
return
}

if allRevisions {
securityProfileName := work.Name[strings.LastIndex(work.Name, "/")+1:]
clilog.Info.Printf("Exporting all the revisions for Security Profile %s\n", securityProfileName)

if respBody, err = ListRevisions(securityProfileName); err != nil {
errs <- err
}
err = json.Unmarshal(respBody, &listsecprofiles)
if err != nil {
errs <- err
}
for _, s := range listsecprofiles.SecurityProfiles {
payload, err := json.Marshal(s)
if err != nil {
errs <- err
}
payload, _ = apiclient.PrettifyJSON(payload)
apiclient.WriteByteArrayToFile(path.Join(folder, s.Name), false, payload)
}
} else {
clilog.Info.Printf("Exporting Security Profile %s\n", work.Name)
payload, err := json.Marshal(work)
if err != nil {
errs <- err
}
payload, _ = apiclient.PrettifyJSON(payload)
apiclient.WriteByteArrayToFile(path.Join(folder, work.Name+".json"), false, payload)
}
}
}

0 comments on commit 446f606

Please sign in to comment.