Skip to content

Commit

Permalink
feat(pkger): add ability to supply a pkg from a url
Browse files Browse the repository at this point in the history
  • Loading branch information
jsteenb2 committed Jan 12, 2020
1 parent 3f56658 commit eb6a4e1
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 19 deletions.
12 changes: 11 additions & 1 deletion cmd/influx/pkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ type cmdPkgBuilder struct {
applyOpts struct {
force string
secrets []string
url string
}
exportOpts struct {
resourceType string
Expand Down Expand Up @@ -97,6 +98,7 @@ func (b *cmdPkgBuilder) cmdPkgApply() *cobra.Command {
cmd.MarkFlagFilename("file", "yaml", "yml", "json")
cmd.Flags().BoolVarP(&b.quiet, "quiet", "q", false, "disable output printing")
cmd.Flags().StringVar(&b.applyOpts.force, "force", "", `TTY input, if package will have destructive changes, proceed if set "true"`)
cmd.Flags().StringVarP(&b.applyOpts.url, "url", "u", "", "URL to retrieve a package.")

b.org.register(cmd, false)

Expand Down Expand Up @@ -132,7 +134,15 @@ func (b *cmdPkgBuilder) pkgApplyRunEFn() func(*cobra.Command, []string) error {
return err
}

pkg, isTTY, err := b.readPkgStdInOrFile(b.file)
var (
pkg *pkger.Pkg
isTTY bool
)
if b.applyOpts.url != "" {
pkg, err = pkger.Parse(pkger.EncodingSource, pkger.FromHTTPRequest(b.applyOpts.url))
} else {
pkg, isTTY, err = b.readPkgStdInOrFile(b.file)
}
if err != nil {
return err
}
Expand Down
21 changes: 21 additions & 0 deletions http/pkger_http_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ type (
ReqApplyPkg struct {
DryRun bool `json:"dryRun" yaml:"dryRun"`
OrgID string `json:"orgID" yaml:"orgID"`
URL string `json:"url" yaml:"url"`
Pkg *pkger.Pkg `json:"package" yaml:"package"`
Secrets map[string]string `json:"secrets"`
}
Expand All @@ -146,6 +147,14 @@ func (s *HandlerPkg) applyPkg(w http.ResponseWriter, r *http.Request) {
return
}

if reqBody.URL != "" && reqBody.Pkg != nil {
s.HandleHTTPError(r.Context(), &influxdb.Error{
Code: influxdb.EInvalid,
Msg: "must provide either url or pkg",
}, w)
return
}

orgID, err := influxdb.IDFromString(reqBody.OrgID)
if err != nil {
s.HandleHTTPError(r.Context(), &influxdb.Error{
Expand All @@ -163,6 +172,18 @@ func (s *HandlerPkg) applyPkg(w http.ResponseWriter, r *http.Request) {
userID := auth.GetUserID()

parsedPkg := reqBody.Pkg
if reqBody.URL != "" {
parsedPkg, err = pkger.Parse(pkger.EncodingSource, pkger.FromHTTPRequest(reqBody.URL))
if err != nil {
s.HandleHTTPError(r.Context(), &influxdb.Error{
Code: influxdb.EInvalid,
Msg: "failed to parse package from provided URL",
Err: err,
}, w)
return
}
}

sum, diff, err := s.svc.DryRun(r.Context(), *orgID, userID, parsedPkg)
if pkger.IsParseErr(err) {
s.encJSONResp(r.Context(), w, http.StatusUnprocessableEntity, RespApplyPkg{
Expand Down
2 changes: 1 addition & 1 deletion pkger/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -1321,7 +1321,7 @@ func (l *label) toInfluxLabel() influxdb.Label {
}

func toSummaryLabels(labels ...*label) []SummaryLabel {
var iLabels []SummaryLabel
iLabels := make([]SummaryLabel, 0, len(labels))
for _, l := range labels {
iLabels = append(iLabels, l.summarize())
}
Expand Down
76 changes: 68 additions & 8 deletions pkger/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"io"
"io/ioutil"
"net/http"
"sort"
"strconv"
"strings"
Expand All @@ -27,15 +28,18 @@ type Encoding int
// encoding types
const (
EncodingUnknown Encoding = iota
EncodingYAML
EncodingJSON
EncodingSource // EncodingSource draws the encoding type by inferring it from the source.
EncodingYAML
)

// String provides the string representation of the encoding.
func (e Encoding) String() string {
switch e {
case EncodingJSON:
return "json"
case EncodingSource:
return "source"
case EncodingYAML:
return "yaml"
default:
Expand All @@ -55,10 +59,12 @@ func Parse(encoding Encoding, readerFn ReaderFn, opts ...ValidateOptFn) (*Pkg, e
}

switch encoding {
case EncodingYAML:
return parseYAML(r, opts...)
case EncodingJSON:
return parseJSON(r, opts...)
case EncodingSource:
return parseSource(r, opts...)
case EncodingYAML:
return parseYAML(r, opts...)
default:
return nil, ErrInvalidEncoding
}
Expand Down Expand Up @@ -93,14 +99,54 @@ func FromString(s string) ReaderFn {
}
}

func parseYAML(r io.Reader, opts ...ValidateOptFn) (*Pkg, error) {
return parse(yaml.NewDecoder(r), opts...)
// FromHTTPRequest parses a pkg from the request body of a HTTP request. This is
// very useful when using packages that are hosted..
func FromHTTPRequest(addr string) ReaderFn {
return func() (io.Reader, error) {
client := http.Client{Timeout: 5 * time.Minute}
resp, err := client.Get(addr)
if err != nil {
return nil, err
}
defer resp.Body.Close()

var buf bytes.Buffer
if _, err := io.Copy(&buf, resp.Body); err != nil {
return nil, err
}
return &buf, nil
}
}

func parseJSON(r io.Reader, opts ...ValidateOptFn) (*Pkg, error) {
return parse(json.NewDecoder(r), opts...)
}

func parseSource(r io.Reader, opts ...ValidateOptFn) (*Pkg, error) {
var b []byte
if byter, ok := r.(interface{ Bytes() []byte }); ok {
b = byter.Bytes()
} else {
bb, err := ioutil.ReadAll(r)
if err != nil {
return nil, fmt.Errorf("failed to decode pkg source: %s", err)
}
b = bb
}

contentType := http.DetectContentType(b)
switch {
case strings.Contains(contentType, "json"):
return parseJSON(bytes.NewReader(b), opts...)
default:
return parseYAML(bytes.NewReader(b), opts...)
}
}

func parseYAML(r io.Reader, opts ...ValidateOptFn) (*Pkg, error) {
return parse(yaml.NewDecoder(r), opts...)
}

type decoder interface {
Decode(interface{}) error
}
Expand Down Expand Up @@ -152,7 +198,20 @@ type Pkg struct {
// associations the pkg contains. It is very useful for informing users of
// the changes that will take place when this pkg would be applied.
func (p *Pkg) Summary() Summary {
var sum Summary
// ensure zero values for arrays aren't returned, but instead
// we always returning an initialized slice.
sum := Summary{
Buckets: []SummaryBucket{},
Checks: []SummaryCheck{},
Dashboards: []SummaryDashboard{},
NotificationEndpoints: []SummaryNotificationEndpoint{},
NotificationRules: []SummaryNotificationRule{},
Labels: []SummaryLabel{},
MissingSecrets: []string{},
Tasks: []SummaryTask{},
TelegrafConfigs: []SummaryTelegraf{},
Variables: []SummaryVariable{},
}

// only add this after dry run has been completed
if p.isVerified {
Expand Down Expand Up @@ -368,8 +427,9 @@ func (p *Pkg) variables() []*variable {
// If a resource does not exist yet, a label mapping will not
// be returned for it.
func (p *Pkg) labelMappings() []SummaryLabelMapping {
var mappings []SummaryLabelMapping
for _, l := range p.mLabels {
labels := p.mLabels
mappings := make([]SummaryLabelMapping, 0, len(labels))
for _, l := range labels {
mappings = append(mappings, l.mappingSummary()...)
}

Expand Down
21 changes: 12 additions & 9 deletions pkger/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,7 @@ func (s *Service) dryRunBuckets(ctx context.Context, orgID influxdb.ID, pkg *Pkg
}
}

var diffs []DiffBucket
diffs := make([]DiffBucket, 0, len(mExistingBkts))
for _, diff := range mExistingBkts {
diffs = append(diffs, diff)
}
Expand Down Expand Up @@ -784,7 +784,7 @@ func (s *Service) dryRunChecks(ctx context.Context, orgID influxdb.ID, pkg *Pkg)
}
}

var diffs []DiffCheck
diffs := make([]DiffCheck, 0, len(mExistingChecks))
for _, diff := range mExistingChecks {
diffs = append(diffs, diff)
}
Expand All @@ -796,8 +796,10 @@ func (s *Service) dryRunChecks(ctx context.Context, orgID influxdb.ID, pkg *Pkg)
}

func (s *Service) dryRunDashboards(pkg *Pkg) []DiffDashboard {
var diffs []DiffDashboard
for _, d := range pkg.dashboards() {
dashs := pkg.dashboards()

diffs := make([]DiffDashboard, 0, len(dashs))
for _, d := range dashs {
diffs = append(diffs, newDiffDashboard(d))
}
return diffs
Expand Down Expand Up @@ -862,7 +864,7 @@ func (s *Service) dryRunNotificationEndpoints(ctx context.Context, orgID influxd
mExistingToNew[newEndpoint.Name()] = newDiffNotificationEndpoint(newEndpoint, existing)
}

var diffs []DiffNotificationEndpoint
diffs := make([]DiffNotificationEndpoint, 0, len(mExistingToNew))
for _, diff := range mExistingToNew {
diffs = append(diffs, diff)
}
Expand All @@ -885,7 +887,7 @@ func (s *Service) dryRunNotificationRules(ctx context.Context, orgID influxdb.ID
mExisting[e.GetName()] = e
}

var diffs []DiffNotificationRule
diffs := make([]DiffNotificationRule, 0, len(mExisting))
for _, r := range pkg.notificationRules() {
e, ok := mExisting[r.endpointName]
if !ok {
Expand Down Expand Up @@ -929,8 +931,9 @@ func (s *Service) dryRunTasks(pkg *Pkg) []DiffTask {
}

func (s *Service) dryRunTelegraf(pkg *Pkg) []DiffTelegraf {
var diffs []DiffTelegraf
for _, t := range pkg.telegrafs() {
telegrafs := pkg.telegrafs()
diffs := make([]DiffTelegraf, 0, len(telegrafs))
for _, t := range telegrafs {
diffs = append(diffs, newDiffTelegraf(t))
}
return diffs
Expand Down Expand Up @@ -1008,7 +1011,7 @@ func (s *Service) dryRunLabelMappings(ctx context.Context, pkg *Pkg) ([]DiffLabe
mapperVariables(pkg.variables()),
}

var diffs []DiffLabelMapping
diffs := make([]DiffLabelMapping, 0)
for _, mapper := range mappers {
for i := 0; i < mapper.Len(); i++ {
la := mapper.Association(i)
Expand Down

0 comments on commit eb6a4e1

Please sign in to comment.