-
Notifications
You must be signed in to change notification settings - Fork 67
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Search Implementation and Query Type
* adds functionality to http.go for encoding querystrings * Consistency fis for JSONClass field name across the board (JsonClass) * new search.go and examples implementing standard search
- Loading branch information
Showing
7 changed files
with
298 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
-----BEGIN RSA PRIVATE KEY----- | ||
MIIEpAIBAAKCAQEAx12nDxxOwSPHRSJEDz67a0folBqElzlu2oGMiUTS+dqtj3FU | ||
h5lJc1MjcprRVxcDVwhsSSo9948XEkk39IdblUCLohucqNMzOnIcdZn8zblN7Cnp | ||
W03UwRM0iWX1HuwHnGvm6PKeqKGqplyIXYO0qlDWCzC+VaxFTwOUk31MfOHJQn4y | ||
fTrfuE7h3FTElLBu065SFp3dPICIEmWCl9DadnxbnZ8ASxYQ9xG7hmZduDgjNW5l | ||
3x6/EFkpym+//D6AbWDcVJ1ovCsJL3CfH/NZC3ekeJ/aEeLxP/vaCSH1VYC5VsYK | ||
5Qg7SIa6Nth3+RZz1hYOoBJulEzwljznwoZYRQIDAQABAoIBADPQol+qAsnty5er | ||
PTcdHcbXLJp5feZz1dzSeL0gdxja/erfEJIhg9aGUBs0I55X69VN6h7l7K8PsHZf | ||
MzzJhUL4QJJETOYP5iuVhtIF0I+DTr5Hck/5nYcEv83KAvgjbiL4ZE486IF5awnL | ||
2OE9HtJ5KfhEleNcX7MWgiIHGb8G1jCqu/tH0GI8Z4cNgUrXMbczGwfbN/5Wc0zo | ||
Dtpe0Tec/Fd0DLFwRiAuheakPjlVWb7AGMDX4TyzCXfMpS1ul2jk6nGFk77uQozF | ||
PQUawCRp+mVS4qecgq/WqfTZZbBlW2L18/kpafvsxG8kJ7OREtrb0SloZNFHEc2Q | ||
70GbgKECgYEA6c/eOrI3Uour1gKezEBFmFKFH6YS/NZNpcSG5PcoqF6AVJwXg574 | ||
Qy6RatC47e92be2TT1Oyplntj4vkZ3REv81yfz/tuXmtG0AylH7REbxubxAgYmUT | ||
18wUAL4s3TST2AlK4R29KwBadwUAJeOLNW+Rc4xht1galsqQRb4pUzkCgYEA2kj2 | ||
vUhKAB7QFCPST45/5q+AATut8WeHnI+t1UaiZoK41Jre8TwlYqUgcJ16Q0H6KIbJ | ||
jlEZAu0IsJxjQxkD4oJgv8n5PFXdc14HcSQ512FmgCGNwtDY/AT7SQP3kOj0Rydg | ||
N02uuRb/55NJ07Bh+yTQNGA+M5SSnUyaRPIAMW0CgYBgVU7grDDzB60C/g1jZk/G | ||
VKmYwposJjfTxsc1a0gLJvSE59MgXc04EOXFNr4a+oC3Bh2dn4SJ2Z9xd1fh8Bur | ||
UwCLwVE3DBTwl2C/ogiN4C83/1L4d2DXlrPfInvloBYR+rIpUlFweDLNuve2pKvk | ||
llU9YGeaXOiHnGoY8iKgsQKBgQDZKMOHtZYhHoZlsul0ylCGAEz5bRT0V8n7QJlw | ||
12+TSjN1F4n6Npr+00Y9ov1SUh38GXQFiLq4RXZitYKu6wEJZCm6Q8YXd1jzgDUp | ||
IyAEHNsrV7Y/fSSRPKd9kVvGp2r2Kr825aqQasg16zsERbKEdrBHmwPmrsVZhi7n | ||
rlXw1QKBgQDBOyUJKQOgDE2u9EHybhCIbfowyIE22qn9a3WjQgfxFJ+aAL9Bg124 | ||
fJIEzz43fJ91fe5lTOgyMF5TtU5ClAOPGtlWnXU0e5j3L4LjbcqzEbeyxvP3sn1z | ||
dYkX7NdNQ5E6tcJZuJCGq0HxIAQeKPf3x9DRKzMnLply6BEzyuAC4g== | ||
-----END RSA PRIVATE KEY----- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"log" | ||
"os" | ||
|
||
"github.com/davecgh/go-spew/spew" | ||
"github.com/go-chef/chef" | ||
) | ||
|
||
func main() { | ||
// read a client key | ||
key, err := ioutil.ReadFile("key.pem") | ||
if err != nil { | ||
fmt.Println("Couldn't read key.pem:", err) | ||
os.Exit(1) | ||
} | ||
|
||
// build a client | ||
client, err := chef.NewClient(&chef.Config{ | ||
Name: "foo", | ||
Key: string(key), | ||
// goiardi is on port 4545 by default. chef-zero is 8889 | ||
BaseURL: "http://localhost:4545", | ||
}) | ||
if err != nil { | ||
fmt.Println("Issue setting up client:", err) | ||
os.Exit(1) | ||
} | ||
|
||
// List Indexes | ||
indexes, err := client.Search.Indexes() | ||
if err != nil { | ||
log.Fatal("Couldn't list nodes: ", err) | ||
} | ||
|
||
// dump the Index list in Json | ||
jsonData, err := json.MarshalIndent(indexes, "", "\t") | ||
os.Stdout.Write(jsonData) | ||
os.Stdout.WriteString("\n") | ||
|
||
// build a seach query | ||
query, err := client.Search.NewQuery("node", "name:*") | ||
if err != nil { | ||
log.Fatal("Error building query ", err) | ||
} | ||
|
||
// Run the query | ||
res, err := query.Do(client) | ||
if err != nil { | ||
log.Fatal("Error running query ", err) | ||
} | ||
|
||
// <3 spew | ||
spew.Dump(res) | ||
|
||
// dump out results back in json for fun | ||
jsonData, err = json.MarshalIndent(res, "", "\t") | ||
os.Stdout.Write(jsonData) | ||
os.Stdout.WriteString("\n") | ||
|
||
// You can also use the service to run a query | ||
res, err = client.Search.Exec("node", "name:*") | ||
if err != nil { | ||
log.Fatal("Error running Search.Exec() ", err) | ||
} | ||
|
||
// dump out results back in json for fun | ||
jsonData, err = json.MarshalIndent(res, "", "\t") | ||
os.Stdout.Write(jsonData) | ||
os.Stdout.WriteString("\n") | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package chef | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"strings" | ||
) | ||
|
||
type SearchService struct { | ||
client *Client | ||
} | ||
|
||
// SearchQuery Is the struct for holding a query request | ||
type SearchQuery struct { | ||
// The index you want to search | ||
Index string | ||
|
||
// The query you want to execute. This is the 'chef' query ex: 'chef_environment:prod' | ||
Query string | ||
|
||
// Sort order you want the search results returned | ||
SortBy string | ||
|
||
// Starting position for search | ||
Start int | ||
|
||
// Number of rows to return | ||
Rows int | ||
} | ||
|
||
// String implements the Stringer Interface for the SearchQuery | ||
func (q SearchQuery) String() string { | ||
return fmt.Sprintf("%s?q=%s&rows=%d&sort=%s&start=%d", q.Index, q.Query, q.Rows, q.SortBy, q.Start) | ||
} | ||
|
||
// search result will return a slice of interface{} of chef-like objects (roles/nodes/etc) | ||
type SearchResult struct { | ||
Total int | ||
Start int | ||
Rows []interface{} | ||
} | ||
|
||
// Do will execute the search query on the client | ||
func (q SearchQuery) Do(client *Client) (res SearchResult, err error) { | ||
fullUrl := fmt.Sprintf("/search/%s", q) | ||
err = client.magicRequestDecoder("GET", fullUrl, nil, &res) | ||
return | ||
} | ||
|
||
// NewSearch is a constructor for a SearchQuery struct. This is used by other search service methods to perform search requests on the server | ||
func (e SearchService) NewQuery(idx, statement string) (query SearchQuery, err error) { | ||
// validate statement | ||
if !strings.Contains(statement, ":") { | ||
err = errors.New("statement is malformed") | ||
return | ||
} | ||
|
||
query = SearchQuery{ | ||
Index: idx, | ||
Query: statement, | ||
// These are the defaults in chef: https://github.com/opscode/chef/blob/master/lib/chef/search/query.rb#L102-L105 | ||
SortBy: "X_CHEF_id_CHEF_X asc", | ||
Start: 0, | ||
Rows: 1000, | ||
} | ||
|
||
return | ||
} | ||
|
||
// Exec runs the query on the index passed in. This is a helper method. If you wnat more controll over the query use NewQuery and its Do() method. | ||
// BUG(spheromak): Should we use exec or SearchQuery.Do() or have both ? | ||
func (e SearchService) Exec(idx, statement string) (res SearchResult, err error) { | ||
// Copy-paste here till We decide which way to go with exec vs Do | ||
if !strings.Contains(statement, ":") { | ||
err = errors.New("statement is malformed") | ||
return | ||
} | ||
|
||
query := SearchQuery{ | ||
Index: idx, | ||
Query: statement, | ||
// These are the defaults in chef: https://github.com/opscode/chef/blob/master/lib/chef/search/query.rb#L102-L105 | ||
SortBy: "X_CHEF_id_CHEF_X asc", | ||
Start: 0, | ||
Rows: 1000, | ||
} | ||
|
||
res, err = query.Do(e.client) | ||
return | ||
} | ||
|
||
// List lists the nodes in the Chef server. | ||
// | ||
// Chef API docs: http://docs.opscode.com/api_chef_server.html#id25 | ||
func (e SearchService) Indexes() (data map[string]string, err error) { | ||
err = e.client.magicRequestDecoder("GET", "search", nil, &data) | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package chef | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
func TestSearch_Get(t *testing.T) { | ||
setup() | ||
defer teardown() | ||
|
||
mux.HandleFunc("/search", func(w http.ResponseWriter, r *http.Request) { | ||
fmt.Fprintf(w, `{ | ||
"node": "http://localhost:4000/search/node", | ||
"role": "http://localhost:4000/search/role", | ||
"client": "http://localhost:4000/search/client", | ||
"users": "http://localhost:4000/search/users" | ||
}`) | ||
}) | ||
|
||
indexes, err := client.Search.Indexes() | ||
if err != nil { | ||
t.Errorf("Search.Get returned error: %+v", err) | ||
} | ||
wantedIdx := map[string]string{ | ||
"node": "http://localhost:4000/search/node", | ||
"role": "http://localhost:4000/search/role", | ||
"client": "http://localhost:4000/search/client", | ||
"users": "http://localhost:4000/search/users", | ||
} | ||
if !reflect.DeepEqual(indexes, wantedIdx) { | ||
t.Errorf("Search.Get returned %+v, want %+v", indexes, wantedIdx) | ||
} | ||
} | ||
|
||
func TestSearch_ExecDo(t *testing.T) { | ||
setup() | ||
defer teardown() | ||
|
||
mux.HandleFunc("/search/nodes", func(w http.ResponseWriter, r *http.Request) { | ||
fmt.Fprintf(w, `{ | ||
"total": 1, | ||
"start": 0, | ||
"rows": [ | ||
{ | ||
"overrides": {"hardware_type": "laptop"}, | ||
"name": "latte", | ||
"chef_type": "node", | ||
"json_class": "Chef::Node", | ||
"attributes": {"hardware_type": "laptop"}, | ||
"run_list": ["recipe[unicorn]"], | ||
"defaults": {} | ||
} | ||
] | ||
}`) | ||
}) | ||
|
||
// test the fail case | ||
_, err := client.Search.NewQuery("foo", "failsauce") | ||
if err == nil { | ||
t.Errorf("Bad query wasn't caught") | ||
} | ||
|
||
// test the positive case | ||
query, err := client.Search.NewQuery("nodes", "name:latte") | ||
if err != nil { | ||
t.Errorf("failed to create query") | ||
} | ||
|
||
// for now we aren't testing the result.. | ||
_, err = query.Do(client) | ||
if err != nil { | ||
t.Errorf("Search.Exec failed", err) | ||
} | ||
|
||
_, err = client.Search.Exec("nodes", "name:latte") | ||
if err != nil { | ||
t.Errorf("Search.Exec failed", err) | ||
} | ||
|
||
} |