systemtest/fleettest/client.go (332 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you 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 fleettest import ( "bytes" "encoding/json" "fmt" "io" "net/http" "net/url" ) // Client provides methods for interacting with the Fleet API. type Client struct { fleetURL string } // NewClient returns a new Client for interacting with the Fleet API, // using the given Kibana URL. func NewClient(kibanaURL string) *Client { return &Client{fleetURL: kibanaURL + "/api/fleet"} } // Setup invokes the Fleet Setup API, returning an error if it fails. func (c *Client) Setup() error { for _, path := range []string{"/setup", "/agents/setup"} { req := c.newFleetRequest("POST", path, nil) resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() if err := consumeResponse(resp, nil); err != nil { return err } } return nil } // Agents returns the list of enrolled agents. func (c *Client) Agents() ([]Agent, error) { resp, err := http.Get(c.fleetURL + "/agents") if err != nil { return nil, err } defer resp.Body.Close() var result struct { List []Agent `json:"list"` } if err := consumeResponse(resp, &result); err != nil { return nil, err } return result.List, nil } // BulkUnenrollAgents bulk-unenrolls agents. func (c *Client) BulkUnenrollAgents(revoke bool, agentIDs ...string) error { var body bytes.Buffer type bulkUnenroll struct { Agents []string `json:"agents"` Revoke bool `json:"revoke"` } if err := json.NewEncoder(&body).Encode(bulkUnenroll{agentIDs, revoke}); err != nil { return err } req := c.newFleetRequest("POST", "/agents/bulk_unenroll", &body) resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() return consumeResponse(resp, nil) } // AgentPolicies returns the Agent Policies matching the given KQL query. func (c *Client) AgentPolicies(kuery string) ([]AgentPolicy, error) { u, err := url.Parse(c.fleetURL + "/agent_policies") if err != nil { return nil, err } query := u.Query() query.Add("kuery", kuery) u.RawQuery = query.Encode() resp, err := http.Get(u.String()) if err != nil { return nil, err } defer resp.Body.Close() var result struct { Items []AgentPolicy `json:"items"` } if err := consumeResponse(resp, &result); err != nil { return nil, err } return result.Items, nil } // DeleteAgentPolicy deletes the Agent Policy with the given ID. func (c *Client) DeleteAgentPolicy(id string) error { var body bytes.Buffer type deleteAgentPolicy struct { ID string `json:"agentPolicyId"` } if err := json.NewEncoder(&body).Encode(deleteAgentPolicy{id}); err != nil { return err } req := c.newFleetRequest("POST", "/agent_policies/delete", &body) resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() return consumeResponse(resp, nil) } // CreateAgentPolicy returns the default Agent Policy. func (c *Client) CreateAgentPolicy(name, namespace, description string) (*AgentPolicy, *EnrollmentAPIKey, error) { var body bytes.Buffer type newAgentPolicy struct { Name string `json:"name,omitempty"` Namespace string `json:"namespace,omitempty"` Description string `json:"description,omitempty"` MonitoringEnabled []string `json:"monitoring_enabled,omitempty"` } if err := json.NewEncoder(&body).Encode(newAgentPolicy{ Name: name, Namespace: namespace, Description: description, MonitoringEnabled: []string{"logs", "metrics"}, }); err != nil { return nil, nil, err } req, err := http.NewRequest("POST", c.fleetURL+"/agent_policies", &body) if err != nil { return nil, nil, err } req.Header.Set("Content-Type", "application/json") req.Header.Set("kbn-xsrf", "1") resp, err := http.DefaultClient.Do(req) if err != nil { return nil, nil, err } defer resp.Body.Close() var result struct { Item AgentPolicy `json:"item"` } if err := consumeResponse(resp, &result); err != nil { return nil, nil, err } enrollmentAPIKey, err := c.getAgentPolicyEnrollmentAPIKey(result.Item.ID) if err != nil { return nil, nil, err } return &result.Item, enrollmentAPIKey, nil } func (c *Client) getAgentPolicyEnrollmentAPIKey(policyID string) (*EnrollmentAPIKey, error) { keys, err := c.enrollmentAPIKeys("policy_id:" + policyID) if err != nil { return nil, err } if n := len(keys); n != 1 { return nil, fmt.Errorf("expected 1 enrollment API key, got %d", n) } resp, err := http.Get(c.fleetURL + "/enrollment-api-keys/" + keys[0].ID) if err != nil { return nil, err } var result struct { Item EnrollmentAPIKey `json:"item"` } if err := consumeResponse(resp, &result); err != nil { return nil, err } return &result.Item, nil } func (c *Client) enrollmentAPIKeys(kuery string) ([]EnrollmentAPIKey, error) { u, err := url.Parse(c.fleetURL + "/enrollment-api-keys") if err != nil { return nil, err } query := u.Query() query.Add("kuery", kuery) u.RawQuery = query.Encode() resp, err := http.Get(u.String()) if err != nil { return nil, err } defer resp.Body.Close() var result struct { Items []EnrollmentAPIKey `json:"list"` } if err := consumeResponse(resp, &result); err != nil { return nil, err } return result.Items, nil } // ListPackages lists all packages available for installation. func (c *Client) ListPackages() ([]Package, error) { resp, err := http.Get(c.fleetURL + "/epm/packages?experimental=true") if err != nil { return nil, err } defer resp.Body.Close() var result struct { Response []Package `json:"response"` } if err := consumeResponse(resp, &result); err != nil { return nil, err } return result.Response, nil } // Package returns information about the package with the given name and version. func (c *Client) Package(name, version string) (*Package, error) { resp, err := http.Get(c.fleetURL + "/epm/packages/" + name + "-" + version) if err != nil { return nil, err } defer resp.Body.Close() var result struct { Response Package `json:"response"` } if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, err } return &result.Response, nil } // InstallPackage installs the package with the given name. func (c *Client) InstallPackage(name, version string) error { req := c.newFleetRequest("POST", "/epm/packages/"+name+"-"+version, nil) resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() return consumeResponse(resp, nil) } // InstallPackageByUpload uploads and installs a package zip. func (c *Client) InstallPackageByUpload(body io.ReadCloser) error { req := c.newFleetRequest("POST", "/epm/packages", body) req.Header.Set("Content-Type", "application/zip") resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() return consumeResponse(resp, nil) } // DeletePackage deletes (uninstalls) the package with the given name and version. func (c *Client) DeletePackage(name, version string) error { req := c.newFleetRequest("DELETE", "/epm/packages/"+name+"-"+version, nil) resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() return consumeResponse(resp, nil) } // ListPackagePolicies returns information about the package policy with the given ID. func (c *Client) ListPackagePolicies() ([]PackagePolicy, error) { resp, err := http.Get(c.fleetURL + "/package_policies") if err != nil { return nil, err } defer resp.Body.Close() var result struct { Items []PackagePolicy `json:"items"` } if err := consumeResponse(resp, &result); err != nil { return nil, err } return result.Items, nil } // PackagePolicy returns information about the package policy with the given ID. func (c *Client) PackagePolicy(id string) (*PackagePolicy, error) { resp, err := http.Get(c.fleetURL + "/package_policies/" + id) if err != nil { return nil, err } defer resp.Body.Close() var result struct { Item PackagePolicy `json:"item"` } if err := consumeResponse(resp, &result); err != nil { return nil, err } return &result.Item, nil } // CreatePackagePolicy adds an integration to a policy. func (c *Client) CreatePackagePolicy(p *PackagePolicy) error { var body bytes.Buffer if err := json.NewEncoder(&body).Encode(p); err != nil { return err } req := c.newFleetRequest("POST", "/package_policies", &body) resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() return consumeResponse(resp, nil) } // UpdatePackagePolicy updates an existing integration package policy. func (c *Client) UpdatePackagePolicy(p *PackagePolicy) error { policyID := p.ID pCopy := *p pCopy.ID = "" p = &pCopy var body bytes.Buffer if err := json.NewEncoder(&body).Encode(p); err != nil { return err } req := c.newFleetRequest("PUT", "/package_policies/"+policyID, &body) resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() return consumeResponse(resp, nil) } // DeletePackagePolicy deletes one or more package policies. func (c *Client) DeletePackagePolicy(ids ...string) error { var params struct { PackagePolicyIDs []string `json:"packagePolicyIds"` } params.PackagePolicyIDs = ids var body bytes.Buffer if err := json.NewEncoder(&body).Encode(params); err != nil { return err } req := c.newFleetRequest("POST", "/package_policies/delete", &body) resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() return consumeResponse(resp, nil) } func (c *Client) newFleetRequest(method string, path string, body io.Reader) *http.Request { req, err := http.NewRequest(method, c.fleetURL+path, body) if err != nil { panic(err) } req.Header.Set("Content-Type", "application/json") req.Header.Set("kbn-xsrf", "1") return req } func consumeResponse(resp *http.Response, out interface{}) error { if resp.StatusCode != http.StatusOK { var e Error if err := json.NewDecoder(resp.Body).Decode(&e); err != nil { return err } return &e } if out != nil { if err := json.NewDecoder(resp.Body).Decode(out); err != nil { return err } } return nil }