Skip to content

Commit 07869aa

Browse files
allmightyspiffGitHub Enterprise
authored andcommitted
Merge pull request #838 from SoftLayer/searchFeature
Search feature
2 parents 203e2ac + f5588b4 commit 07869aa

36 files changed

Lines changed: 1288 additions & 505 deletions

README.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -159,17 +159,22 @@ Check testhelpers/fake_softlayer_session.go for all the fields that get recorded
159159

160160
### Fake Managers
161161

162-
CLI calls to manager functions need an entry in `bluemix-cli\bluemix\slplugin\testhelpers\fake_manager.go`
162+
CLI calls to manager functions need an entry in `plugin\testhelpers\fake_manager.go`
163+
Managers have a fake/test interface that is autogenerate with a program called [couterfieter](https://github.com/maxbrunsfeld/counterfeiter)
163164

165+
```
166+
go generate ./...
167+
```
164168

165-
Managers have a fake/test interface that is autogenerate with a program called [couterfieter](https://github.com/maxbrunsfeld/counterfeiter)
169+
170+
each manager and defined interface should have this line in it to be automatically generated. After the imports, before any interfaces
166171

167172
```
168-
# From /github.ibm.com/SoftLayer/softlayer-cli
169-
cd plugin/managers
170-
counterfeiter.exe -o ../testhelpers/fake_storage_manager.go . StorageManager
173+
//counterfeiter:generate -o ../testhelpers/ . <Whatever>Manager
171174
```
172175

176+
177+
173178
If you want to use the real manager but fixture API data, just initialize the manager like this in the CLI test
174179

175180
(filenames here is optional of course)

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ require (
3434
github.com/mattn/go-colorable v0.1.8 // indirect
3535
github.com/mattn/go-isatty v0.0.12 // indirect
3636
github.com/mattn/go-runewidth v0.0.12 // indirect
37+
github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1 // indirect
3738
github.com/nicksnyder/go-i18n/v2 v2.2.0 // indirect
3839
github.com/pelletier/go-toml v1.2.0 // indirect
3940
github.com/pmezard/go-difflib v1.0.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHX
4848
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
4949
github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow=
5050
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
51+
github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1 h1:NicmruxkeqHjDv03SfSxqmaLuisddudfP3h5wdXFbhM=
52+
github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1/go.mod h1:eyp4DdUJAKkr9tvxR3jWhw2mDK7CWABMG5r9uyaKC7I=
5153
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
5254
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
5355
github.com/nicksnyder/go-i18n v1.10.1 h1:isfg77E/aCD7+0lD/D00ebR2MV5vgeQ276WYyDaCRQc=

plugin/commands/bandwidth/summary_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ var _ = Describe("bandwidth summary", func() {
5454

5555
Context("Advanced search to bandwidth summary", func() {
5656
BeforeEach(func() {
57-
filename := []string{"bandwidth"}
57+
filename := []string{"advancedSearch-bandwidth"}
5858
fakeSession = testhelpers.NewFakeSoftlayerSession(filename)
5959
slCommand = metadata.NewSoftlayerCommand(fakeUI, fakeSession)
6060
cliCommand = bandwidth.NewSummaryCommand(slCommand)

plugin/commands/ipsec/detail.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,13 @@ func (cmd *DetailCommand) Run(args []string) error {
113113
return nil
114114
}
115115

116-
//Yields a mask for a tunnel context
117-
//All exposed properties on the tunnel context service are included inthe constructed mask. Additional joins may be requested.
118-
//addressTranslations: Whether to join the context's address translation entries.
119-
//internalSubnets: Whether to join the context's internal subnet associations.
120-
//remoteSubnets: Whether to join the context's remote subnet associations.
121-
//staticSubnets: Whether to join the context's statically routed subnet associations.
122-
//serviceSubnets: Whether to join the SoftLayer service network subnets.
116+
// Yields a mask for a tunnel context
117+
// All exposed properties on the tunnel context service are included inthe constructed mask. Additional joins may be requested.
118+
// addressTranslations: Whether to join the context's address translation entries.
119+
// internalSubnets: Whether to join the context's internal subnet associations.
120+
// remoteSubnets: Whether to join the context's remote subnet associations.
121+
// staticSubnets: Whether to join the context's statically routed subnet associations.
122+
// serviceSubnets: Whether to join the SoftLayer service network subnets.
123123
func GetTunnelContextMask(addressTranslation, internalSubnets, remoteSubnets, statusSubnets, serviceSubnets bool) string {
124124
mask := "id,accountId,advancedConfigurationFlag,createDate,customerPeerIpAddress,modifyDate,name,friendlyName,internalPeerIpAddress" +
125125
",phaseOneAuthentication,phaseOneDiffieHellmanGroup,phaseOneEncryption,phaseOneKeylife" +

plugin/commands/order/item_list.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func (cmd *ItemListCommand) Run(args []string) error {
6363
return nil
6464
}
6565

66-
//"""sorts the items into a dictionary of categories, with a list of items"""
66+
// """sorts the items into a dictionary of categories, with a list of items"""
6767
func sortItems(items []datatypes.Product_Item) map[string][]datatypes.Product_Item {
6868

6969
sortedItems := make(map[string][]datatypes.Product_Item)

plugin/commands/reports/bandwidth_test.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ var _ = Describe("reports bandwidth", func() {
5454

5555
Context("Advanced search to bandwidth summary", func() {
5656
BeforeEach(func() {
57-
filename := []string{"bandwidth"}
57+
filename := []string{"advancedSearch-bandwidth"}
5858
fakeSession = testhelpers.NewFakeSoftlayerSession(filename)
5959
slCommand = metadata.NewSoftlayerCommand(fakeUI, fakeSession)
6060
cliCommand = reports.NewBandwidthCommand(slCommand)
@@ -85,10 +85,7 @@ var _ = Describe("reports bandwidth", func() {
8585
Expect(fakeUI.Outputs()).To(ContainSubstring(`"Allocation": "250.00 GB",`))
8686
Expect(fakeUI.Outputs()).To(ContainSubstring(`"Pool": "Virtual Private Rack",`))
8787
Expect(fakeUI.Outputs()).To(ContainSubstring(`"Tags": ""`))
88-
Expect(fakeUI.Outputs()).To(ContainSubstring(`[`))
89-
Expect(fakeUI.Outputs()).To(ContainSubstring(`{`))
90-
Expect(fakeUI.Outputs()).To(ContainSubstring(`}`))
91-
Expect(fakeUI.Outputs()).To(ContainSubstring(`]`))
88+
9289
})
9390
})
9491
})

plugin/commands/search/search.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package search
2+
3+
import (
4+
"fmt"
5+
"github.com/IBM-Cloud/ibm-cloud-cli-sdk/plugin"
6+
"github.com/spf13/cobra"
7+
"strings"
8+
9+
"github.com/softlayer/softlayer-go/datatypes"
10+
. "github.ibm.com/SoftLayer/softlayer-cli/plugin/i18n"
11+
"github.ibm.com/SoftLayer/softlayer-cli/plugin/managers"
12+
"github.ibm.com/SoftLayer/softlayer-cli/plugin/metadata"
13+
14+
"github.ibm.com/SoftLayer/softlayer-cli/plugin/utils"
15+
)
16+
17+
type SearchCommand struct {
18+
*metadata.SoftlayerCommand
19+
SearchManager managers.SearchManager
20+
Command *cobra.Command
21+
Query string
22+
}
23+
24+
func SetupCobraCommands(sl *metadata.SoftlayerCommand) *cobra.Command {
25+
thisCmd := &SearchCommand{
26+
SoftlayerCommand: sl,
27+
SearchManager: managers.NewSearchManager(sl.Session),
28+
}
29+
cobraCmd := &cobra.Command{
30+
Use: "search",
31+
Short: T("Perform a query against the SoftLayer search database."),
32+
Long: T(`Read More: https://sldn.softlayer.com/reference/services/SoftLayer_Search/search/
33+
Examples::
34+
35+
sl search --query 'test.com'
36+
sl search --query '_objectType:SoftLayer_Virtual_Guest test.com'
37+
`),
38+
Args: metadata.NoArgs,
39+
RunE: func(cmd *cobra.Command, args []string) error {
40+
return thisCmd.Run(args)
41+
},
42+
}
43+
cobraCmd.Flags().StringVarP(&thisCmd.Query, "query", "q", "", T("The search query you want to use."))
44+
cobraCmd.AddCommand(NewSearchTypesCommand(sl).Command)
45+
return cobraCmd
46+
}
47+
48+
func SearchNamespace() plugin.Namespace {
49+
return plugin.Namespace{
50+
ParentName: "sl",
51+
Name: "search",
52+
Description: T("Perform a query against the SoftLayer search database."),
53+
}
54+
}
55+
56+
func (cmd *SearchCommand) Run(args []string) error {
57+
58+
results, err := cmd.SearchManager.AdvancedSearch("", cmd.Query)
59+
if err != nil {
60+
return err
61+
}
62+
if cmd.GetOutputFlag() == "JSON" {
63+
return utils.PrintPrettyJSON(cmd.UI, results)
64+
}
65+
table := cmd.UI.Table([]string{"Type", "Matched Terms", "Resource"})
66+
for _, result := range results {
67+
resource_string := ""
68+
switch *result.ResourceType {
69+
case "SoftLayer_Virtual_Guest":
70+
resource_object := result.Resource.(*datatypes.Virtual_Guest)
71+
resource_string = parseVirtual_Guest(*resource_object)
72+
case "SoftLayer_Event_Log":
73+
resource_object := result.Resource.(*datatypes.Event_Log)
74+
resource_string = parseEvent_Log(*resource_object)
75+
case "SoftLayer_Virtual_DedicatedHost":
76+
resource_object := result.Resource.(*datatypes.Virtual_DedicatedHost)
77+
resource_string = parseVirtual_DedicatedHost(*resource_object)
78+
case "SoftLayer_Hardware":
79+
resource_object := result.Resource.(*datatypes.Hardware)
80+
resource_string = parseHardware(*resource_object)
81+
case "SoftLayer_Network_Application_Delivery_Controller":
82+
resource_object := result.Resource.(*datatypes.Network_Application_Delivery_Controller)
83+
resource_string = parseNetwork_Application_Delivery_Controller(*resource_object)
84+
case "SoftLayer_Network_Subnet_IpAddress":
85+
resource_object := result.Resource.(*datatypes.Network_Subnet_IpAddress)
86+
resource_string = parseNetwork_Subnet_IpAddress(*resource_object)
87+
case "SoftLayer_Network_Vlan":
88+
resource_object := result.Resource.(*datatypes.Network_Vlan)
89+
resource_string = parseNetwork_Vlan(*resource_object)
90+
case "SoftLayer_Network_Vlan_Firewall":
91+
resource_object := result.Resource.(*datatypes.Network_Vlan_Firewall)
92+
resource_string = parseNetwork_Vlan_Firewall(*resource_object)
93+
case "SoftLayer_Ticket":
94+
resource_object := result.Resource.(*datatypes.Ticket)
95+
resource_string = parseTicket(*resource_object)
96+
}
97+
table.Add(*result.ResourceType, parseMatchedTerms(result), resource_string)
98+
}
99+
table.Print()
100+
return nil
101+
}
102+
103+
func parseMatchedTerms(searchResult datatypes.Container_Search_Result) string {
104+
return strings.Join(searchResult.MatchedTerms, "\n")
105+
}
106+
func parseVirtual_Guest(resource datatypes.Virtual_Guest) string {
107+
return fmt.Sprintf(T("ID")+": %d\n"+T("FQDN")+": %s\n", *resource.Id, *resource.FullyQualifiedDomainName)
108+
}
109+
func parseEvent_Log(resource datatypes.Event_Log) string {
110+
return fmt.Sprintf(T("ID")+": %s\n"+T("Event")+": %s\n", *resource.TraceId, *resource.EventName)
111+
}
112+
func parseVirtual_DedicatedHost(resource datatypes.Virtual_DedicatedHost) string {
113+
return fmt.Sprintf(T("ID")+": %d\n"+T("Name")+": %s\n", *resource.Id, *resource.Name)
114+
}
115+
func parseHardware(resource datatypes.Hardware) string {
116+
return fmt.Sprintf(T("ID")+": %d\n"+T("FQDN")+": %s\n", *resource.Id, *resource.FullyQualifiedDomainName)
117+
}
118+
func parseNetwork_Application_Delivery_Controller(resource datatypes.Network_Application_Delivery_Controller) string {
119+
return fmt.Sprintf(T("ID")+": %d\n"+T("Name")+": %s\n", *resource.Id, *resource.Name)
120+
}
121+
func parseNetwork_Subnet_IpAddress(resource datatypes.Network_Subnet_IpAddress) string {
122+
return fmt.Sprintf(T("ID")+": %d\n"+T("Ip Address")+": %s\n", *resource.Id, *resource.IpAddress)
123+
}
124+
func parseNetwork_Vlan(resource datatypes.Network_Vlan) string {
125+
return fmt.Sprintf(T("ID")+": %d\n"+T("VLAN")+": %d\n", *resource.Id, *resource.VlanNumber)
126+
}
127+
func parseNetwork_Vlan_Firewall(resource datatypes.Network_Vlan_Firewall) string {
128+
return fmt.Sprintf(T("ID")+": %d\n"+T("Ip Address")+": %s\n", *resource.Id, *resource.PrimaryIpAddress)
129+
}
130+
func parseTicket(resource datatypes.Ticket) string {
131+
return fmt.Sprintf(T("ID")+": %d\n"+T("Subject")+": %s\n", *resource.Id, *resource.Title)
132+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package search_test
2+
3+
import (
4+
"testing"
5+
6+
. "github.com/onsi/ginkgo/v2"
7+
. "github.com/onsi/gomega"
8+
9+
"github.com/IBM-Cloud/ibm-cloud-cli-sdk/testhelpers/terminal"
10+
"github.ibm.com/SoftLayer/softlayer-cli/plugin/commands/search"
11+
"github.ibm.com/SoftLayer/softlayer-cli/plugin/metadata"
12+
"github.ibm.com/SoftLayer/softlayer-cli/plugin/testhelpers"
13+
"github.ibm.com/SoftLayer/softlayer-cli/plugin/utils"
14+
)
15+
16+
func TestManagers(t *testing.T) {
17+
RegisterFailHandler(Fail)
18+
RunSpecs(t, "SEarch Suite")
19+
}
20+
21+
var availableCommands = []string{
22+
"types",
23+
}
24+
25+
// This test suite exists to make sure commands don't get accidently removed from the actionBindings
26+
var _ = Describe("Test search commands", func() {
27+
fakeUI := terminal.NewFakeUI()
28+
fakeSession := testhelpers.NewFakeSoftlayerSession([]string{"advancedSearch-allTypes"})
29+
slMeta := metadata.NewSoftlayerCommand(fakeUI, fakeSession)
30+
cliCommand := search.SetupCobraCommands(slMeta)
31+
cliCommand.PersistentFlags().Var(slMeta.OutputFlag, "output", "--output=JSON for json output.")
32+
// fakeSearchManager := new(testhelpers.FakeSearchManager)
33+
34+
Context("New commands testable", func() {
35+
commands := search.SetupCobraCommands(slMeta)
36+
37+
var arrayCommands = []string{}
38+
for _, command := range commands.Commands() {
39+
commandName := command.Name()
40+
arrayCommands = append(arrayCommands, commandName)
41+
It("available commands "+commands.Name(), func() {
42+
available := false
43+
if utils.StringInSlice(commandName, availableCommands) != -1 {
44+
available = true
45+
}
46+
Expect(available).To(BeTrue(), commandName+" not found in array available Commands")
47+
})
48+
}
49+
for _, command := range availableCommands {
50+
commandName := command
51+
It("ibmcloud sl "+commands.Name(), func() {
52+
available := false
53+
if utils.StringInSlice(commandName, arrayCommands) != -1 {
54+
available = true
55+
}
56+
Expect(available).To(BeTrue(), commandName+" not found in ibmcloud sl "+commands.Name())
57+
})
58+
}
59+
})
60+
61+
Context("Seach Namespace", func() {
62+
It("Search Name Space", func() {
63+
Expect(search.SearchNamespace().ParentName).To(ContainSubstring("sl"))
64+
Expect(search.SearchNamespace().Name).To(ContainSubstring("search"))
65+
Expect(search.SearchNamespace().Description).To(ContainSubstring("Perform a query against the SoftLayer search database."))
66+
})
67+
})
68+
69+
var fakeHandler *testhelpers.FakeTransportHandler
70+
BeforeEach(func() {
71+
fakeHandler = testhelpers.GetSessionHandler(fakeSession)
72+
})
73+
// Search command is a bit special is an actual command, not a command group like most others.
74+
Context("Search Command tests", func() {
75+
It("Basic Search Command", func() {
76+
err := testhelpers.RunCobraCommand(cliCommand)
77+
Expect(err).NotTo(HaveOccurred())
78+
})
79+
It("Basic Search API Error", func() {
80+
fakeHandler.AddApiError("SoftLayer_Search", "advancedSearch", 500, "Search Error")
81+
err := testhelpers.RunCobraCommand(cliCommand)
82+
Expect(err).To(HaveOccurred())
83+
Expect(err.Error()).To(ContainSubstring("Search Error: Search Error (HTTP 500)"))
84+
})
85+
It("Basic Search Command with query", func() {
86+
err := testhelpers.RunCobraCommand(cliCommand, "-q", `"_objectTpye:SoftLayer_Virtual_Guest test.com`)
87+
Expect(err).NotTo(HaveOccurred())
88+
Expect(fakeUI.Outputs()).To(ContainSubstring("SoftLayer_Network_Vlan"))
89+
Expect(fakeUI.Outputs()).To(ContainSubstring("VLAN |match| ID: 675037"))
90+
})
91+
It("Basic Search Command with JSON output", func() {
92+
err := testhelpers.RunCobraCommand(cliCommand, "--output=JSON", "-q", `"test.com"`)
93+
Expect(err).NotTo(HaveOccurred())
94+
Expect(fakeUI.Outputs()).To(ContainSubstring(`"resourceType": "SoftLayer_Ticket"`))
95+
Expect(fakeUI.Outputs()).To(ContainSubstring(`"id": 85346218,`))
96+
})
97+
})
98+
AfterEach(func() {
99+
fakeHandler.ClearApiCallLogs()
100+
fakeHandler.ClearErrors()
101+
})
102+
})

0 commit comments

Comments
 (0)