diff --git a/Gopkg.lock b/Gopkg.lock index c2a8443715f93..f1544750b038e 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -673,6 +673,17 @@ pruneopts = "UT" revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" +[[projects]] + digest = "1:2d0b44ee6208d256e29f55764db1fa41e3ae33fac6cef138acae6a79c3bd748e" + name = "github.com/magefile/mage" + packages = [ + "mg", + "sh", + ] + pruneopts = "UT" + revision = "aedfce64c122eef47009b7f80c9771044753215d" + version = "v1.8.0" + [[projects]] digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67" name = "github.com/mattn/go-colorable" @@ -1573,6 +1584,7 @@ "github.com/docker/docker/api/types/plugins/logdriver", "github.com/docker/docker/daemon/logger", "github.com/docker/docker/daemon/logger/jsonfilelog", + "github.com/docker/docker/daemon/logger/templates", "github.com/docker/docker/pkg/ioutils", "github.com/docker/go-plugins-helpers/sdk", "github.com/fatih/color", @@ -1588,6 +1600,8 @@ "github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc", "github.com/hpcloud/tail", "github.com/jmespath/go-jmespath", + "github.com/magefile/mage/mg", + "github.com/magefile/mage/sh", "github.com/mitchellh/mapstructure", "github.com/mwitkow/go-grpc-middleware", "github.com/opentracing/opentracing-go", @@ -1611,12 +1625,12 @@ "github.com/shurcooL/httpfs/filter", "github.com/shurcooL/httpfs/union", "github.com/shurcooL/vfsgen", - "github.com/sirupsen/logrus", "github.com/stretchr/testify/assert", "github.com/stretchr/testify/require", "github.com/tonistiigi/fifo", "github.com/weaveworks/common/httpgrpc", "github.com/weaveworks/common/httpgrpc/server", + "github.com/weaveworks/common/logging", "github.com/weaveworks/common/middleware", "github.com/weaveworks/common/server", "github.com/weaveworks/common/tracing", @@ -1624,6 +1638,7 @@ "golang.org/x/net/context", "google.golang.org/grpc", "google.golang.org/grpc/health/grpc_health_v1", + "google.golang.org/grpc/metadata", "gopkg.in/alecthomas/kingpin.v2", "gopkg.in/fsnotify.v1", "gopkg.in/yaml.v2", diff --git a/Makefile b/Makefile index ff74cc07b6a22..3cf651fe6992e 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -.PHONY: all test clean images protos assets check_assets release-prepare release-perform +.PHONY: all test clean images protos assets check_assets release-prepare release-perform mage mage-remove mage-upgrade .DEFAULT_GOAL := all CHARTS := production/helm/loki production/helm/promtail production/helm/loki-stack @@ -312,3 +312,16 @@ push-plugin: build-plugin enable-plugin: docker plugin enable grafana/loki-docker-driver:$(PLUGIN_TAG) + +$(GOPATH)/bin/mage: + go get -u -d github.com/magefile/mage + cd $(GOPATH)/src/github.com/magefile/mage && go run bootstrap.go + +mage: $(GOPATH)/bin/mage + @echo "\n>> MAGE VERSION" + mage -version + +mage-remove: + rm $(GOPATH)/bin/mage + +mage-upgrade: mage-remove mage diff --git a/mage/assets.go b/mage/assets.go new file mode 100644 index 0000000000000..206bc8a15e33c --- /dev/null +++ b/mage/assets.go @@ -0,0 +1,28 @@ +package magefile + +import ( + "os" + + "github.com/magefile/mage/mg" + "github.com/magefile/mage/sh" + "github.com/pkg/errors" +) + +type Assets mg.Namespace + +func (Assets) CheckAssets() error { + err := sh.Run("git", "diff", "--exit-code", "pkg/promtail/server/ui") + if err != nil { + return errors.Wrap(err, "assets have been changed, run `mage assets:generateAssets` and then commit changes to continue") + } + return err +} + +func (Assets) GenerateAssets() error { + return sh.RunWith( + map[string]string{ + "GOOS": os.Getenv("GOOS"), + }, + "go", "generate", "-x", "-v", "./pkg/promtail/server/ui", + ) +} diff --git a/mage/build.go b/mage/build.go new file mode 100644 index 0000000000000..f3caa5d2ae91d --- /dev/null +++ b/mage/build.go @@ -0,0 +1,70 @@ +package magefile + +import ( + "fmt" + "log" + "os" + "strings" + + "github.com/magefile/mage/mg" + "github.com/magefile/mage/sh" +) + +const vendor = "github.com/grafana/loki/vendor/" + +// Build Namespace groups all targets related to building applications +type Build mg.Namespace + +func (Build) Promtail() error { + return compile("promtail") +} + +func (Build) Loki() error { + return compile("loki") +} + +func (Build) Logcli() error { + return compile("logcli") +} + +func compile(app string) error { + debug := os.Getenv("DEBUG") != "" + log.Printf("Building %v, debug=%v", app, debug) + + var flags = map[string]string{ + "ldflags": strings.Join(ldflags(debug), " "), + "tags": "netgo", + "o": fmt.Sprintf("cmd/%s/%s", app, app), + } + + if debug { + flags["gcflags"] = "all=-N -l" + } + + flagSlice := joinMap(flags, "-%v=%v") + args := append(flagSlice, "./cmd/"+app) + + return sh.RunWith( + map[string]string{ + "CGO_ENABLED": "0", + "GOOS": os.Getenv("GOOS"), + "GOARCH": os.Getenv("GOARCH"), + }, + "go", append([]string{"build"}, args...)..., + ) +} + +func ldflags(debug bool) (flags []string) { + var vars = joinMap(map[string]string{ + vendor + "github.com/prometheus/common/version.Branch": revParse("--short", "HEAD"), + vendor + "github.com/prometheus/common/version.Version": stdout("./tools/image-tag"), + vendor + "github.com/prometheus/common/version.Revision": revParse("--abbrev-ref", "HEAD"), + }, "-X %v=%v") + + flags = []string{`-extldflags "-static"`} + if !debug { + flags = append(flags, "-s", "-w") + } + flags = append(flags, vars...) + return flags +} diff --git a/mage/cmds.go b/mage/cmds.go new file mode 100644 index 0000000000000..e966cab5c8388 --- /dev/null +++ b/mage/cmds.go @@ -0,0 +1,9 @@ +package magefile + +import "github.com/magefile/mage/sh" + +var ( + helm = sh.RunCmd("helm") + git = sh.RunCmd("git") + kubectl = sh.RunCmd("kubectl") +) diff --git a/mage/helm.go b/mage/helm.go new file mode 100644 index 0000000000000..af232a06c4dad --- /dev/null +++ b/mage/helm.go @@ -0,0 +1,142 @@ +package magefile + +import ( + "fmt" + "log" + "os" + + "github.com/magefile/mage/mg" + "github.com/magefile/mage/sh" +) + +type Helm mg.Namespace + +var CHARTS = [...]string{"production/helm/loki", "production/helm/promtail", "production/helm/loki-stack"} + +func (Helm) Build() error { + if err := rmGlob("production/helm/*/requirements.lock"); err != nil { + return err + } + if err := helm("init", "-c"); err != nil { + return err + } + + for _, chart := range CHARTS { + if err := helm("dependency", "build", chart); err != nil { + return err + } + if err := helm("lint", chart); err != nil { + return err + } + if err := helm("package", chart); err != nil { + return err + } + } + + if err := rmGlob("production/helm/*/requirements.lock"); err != nil { + return err + } + + return nil +} + +func (Helm) Publish() error { + if err := sh.Copy("production/helm/README.md", "index.md"); err != nil { + return err + } + + if os.Getenv("CI") != "" { + log.Println("Running in CI, setting up git") + user := os.Getenv("CIRCLE_USERNAME") + if err := git("config", "user.email", fmt.Sprintf("%v@users.noreply.github.com", user)); err != nil { + return err + } + if err := git("config", "user.name", user); err != nil { + return err + } + } + + if err := git("checkout", "gh-pages"); err != nil { + log.Println("gh-pages checkout failed, switching to orphan mode") + if err := git("checkout", "--orphan", "gh-pages"); err != nil { + return err + } + if err := sh.Rm("."); err != nil { + return err + } + } + if _, err := os.Stat("charts"); os.IsNotExist(err) { + if err := os.Mkdir("charts", 0755); err != nil { + return err + } + } + + if err := mvGlobToDir("*.tgz", "charts"); err != nil { + return err + } + if err := os.Rename("index.md", "charts/index.md"); err != nil { + return err + } + + if err := helm("repo", "index", "charts/"); err != nil { + return err + } + + if err := git("add", "charts"); err != nil { + return err + } + + if err := git("commit", "-m", + fmt.Sprintf("[skip ci] Publishing helm charts: %v", os.Getenv("CIRCLE_SHA1")), + ); err != nil { + return err + } + + if err := git("push", "origin", "gh-pages"); err != nil { + return err + } + return nil +} + +func (h Helm) Install() error { + if err := kubectl("apply", "-f", "tools/helm.yaml"); err != nil { + return err + } + + if err := helm("init", "--wait", "--service-account=helm", "--upgrade"); err != nil { + return err + } + + return h.upgrade() +} + +func (h Helm) Debug() error { + return h.upgrade("--dry-run", "--debug") +} + +func (h Helm) Upgrade() error { + return h.upgrade() +} + +func (Helm) upgrade(flags ...string) error { + tag := func(name string) string { + return name + } + + args := []string{"upgrade", "--wait", "--install", "-f=tools/dev.values.yaml"} + args = append(args, flags...) + args = append(args, "loki-stack", "./production/helm/loki-stack") + args = append(args, []string{ + "--set", tag("promtail"), + "--set", tag("loki"), + }...) + + if err := helm(args...); err != nil { + return err + } + return nil +} + +func (Helm) Clean() error { + return helm("delete", "--purge", "loki-stack") +} diff --git a/mage/util.go b/mage/util.go new file mode 100644 index 0000000000000..1568112859b6c --- /dev/null +++ b/mage/util.go @@ -0,0 +1,54 @@ +package magefile + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/magefile/mage/sh" +) + +func rmGlob(glob string) error { + files, err := filepath.Glob(glob) + if err != nil { + return err + } + for _, f := range files { + if err := sh.Rm(f); err != nil { + return err + } + } + return nil +} + +func mvGlobToDir(glob, dir string) error { + files, err := filepath.Glob(glob) + if err != nil { + return err + } + for _, f := range files { + if err := os.Rename(f, filepath.Join(dir, f)); err != nil { + return err + } + } + return nil +} + +func revParse(args ...string) string { + return stdout("git", append([]string{"rev-parse"}, args...)...) +} + +func joinMap(mapping map[string]string, pattern string) (out []string) { + for k, v := range mapping { + out = append(out, fmt.Sprintf(pattern, k, v)) + } + return +} + +func stdout(cmd string, args ...string) string { + out, err := sh.Output(cmd, args...) + if err != nil { + panic(err) + } + return out +} diff --git a/magefile.go b/magefile.go new file mode 100644 index 0000000000000..c4834d7893342 --- /dev/null +++ b/magefile.go @@ -0,0 +1,30 @@ +// +build mage + +package main + +import ( + "github.com/magefile/mage/mg" + "github.com/magefile/mage/sh" + + // mage:import + "github.com/grafana/loki/mage" +) + +func All() error { + mg.Deps( + magefile.Build.Promtail, + magefile.Build.Logcli, + magefile.Build.Loki, + Lint, + Test, + ) + return nil +} + +func Lint() error { + return sh.RunWith(map[string]string{"GOGC": "20"}, "golangci-lint", "run") +} + +func Test() error { + return sh.Run("go", "test", "-p=8", "./...") +} diff --git a/vendor/github.com/magefile/mage/LICENSE b/vendor/github.com/magefile/mage/LICENSE new file mode 100644 index 0000000000000..8dada3edaf50d --- /dev/null +++ b/vendor/github.com/magefile/mage/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/vendor/github.com/magefile/mage/mg/deps.go b/vendor/github.com/magefile/mage/mg/deps.go new file mode 100644 index 0000000000000..67b370eca2471 --- /dev/null +++ b/vendor/github.com/magefile/mage/mg/deps.go @@ -0,0 +1,337 @@ +package mg + +import ( + "context" + "fmt" + "log" + "os" + "reflect" + "runtime" + "strings" + "sync" +) + +// funcType indicates a prototype of build job function +type funcType int + +// funcTypes +const ( + invalidType funcType = iota + voidType + errorType + contextVoidType + contextErrorType + namespaceVoidType + namespaceErrorType + namespaceContextVoidType + namespaceContextErrorType +) + +var logger = log.New(os.Stderr, "", 0) + +type onceMap struct { + mu *sync.Mutex + m map[string]*onceFun +} + +func (o *onceMap) LoadOrStore(s string, one *onceFun) *onceFun { + defer o.mu.Unlock() + o.mu.Lock() + + existing, ok := o.m[s] + if ok { + return existing + } + o.m[s] = one + return one +} + +var onces = &onceMap{ + mu: &sync.Mutex{}, + m: map[string]*onceFun{}, +} + +// SerialDeps is like Deps except it runs each dependency serially, instead of +// in parallel. This can be useful for resource intensive dependencies that +// shouldn't be run at the same time. +func SerialDeps(fns ...interface{}) { + types := checkFns(fns) + ctx := context.Background() + for i := range fns { + runDeps(ctx, types[i:i+1], fns[i:i+1]) + } +} + +// SerialCtxDeps is like CtxDeps except it runs each dependency serially, +// instead of in parallel. This can be useful for resource intensive +// dependencies that shouldn't be run at the same time. +func SerialCtxDeps(ctx context.Context, fns ...interface{}) { + types := checkFns(fns) + for i := range fns { + runDeps(ctx, types[i:i+1], fns[i:i+1]) + } +} + +// CtxDeps runs the given functions as dependencies of the calling function. +// Dependencies must only be of type: +// func() +// func() error +// func(context.Context) +// func(context.Context) error +// Or a similar method on a mg.Namespace type. +// +// The function calling Deps is guaranteed that all dependent functions will be +// run exactly once when Deps returns. Dependent functions may in turn declare +// their own dependencies using Deps. Each dependency is run in their own +// goroutines. Each function is given the context provided if the function +// prototype allows for it. +func CtxDeps(ctx context.Context, fns ...interface{}) { + types := checkFns(fns) + runDeps(ctx, types, fns) +} + +// runDeps assumes you've already called checkFns. +func runDeps(ctx context.Context, types []funcType, fns []interface{}) { + mu := &sync.Mutex{} + var errs []string + var exit int + wg := &sync.WaitGroup{} + for i, f := range fns { + fn := addDep(ctx, types[i], f) + wg.Add(1) + go func() { + defer func() { + if v := recover(); v != nil { + mu.Lock() + if err, ok := v.(error); ok { + exit = changeExit(exit, ExitStatus(err)) + } else { + exit = changeExit(exit, 1) + } + errs = append(errs, fmt.Sprint(v)) + mu.Unlock() + } + wg.Done() + }() + if err := fn.run(); err != nil { + mu.Lock() + errs = append(errs, fmt.Sprint(err)) + exit = changeExit(exit, ExitStatus(err)) + mu.Unlock() + } + }() + } + + wg.Wait() + if len(errs) > 0 { + panic(Fatal(exit, strings.Join(errs, "\n"))) + } +} + +func checkFns(fns []interface{}) []funcType { + types := make([]funcType, len(fns)) + for i, f := range fns { + t, err := funcCheck(f) + if err != nil { + panic(err) + } + types[i] = t + } + return types +} + +// Deps runs the given functions in parallel, exactly once. Dependencies must +// only be of type: +// func() +// func() error +// func(context.Context) +// func(context.Context) error +// Or a similar method on a mg.Namespace type. +// +// This is a way to build up a tree of dependencies with each dependency +// defining its own dependencies. Functions must have the same signature as a +// Mage target, i.e. optional context argument, optional error return. +func Deps(fns ...interface{}) { + CtxDeps(context.Background(), fns...) +} + +func changeExit(old, new int) int { + if new == 0 { + return old + } + if old == 0 { + return new + } + if old == new { + return old + } + // both different and both non-zero, just set + // exit to 1. Nothing more we can do. + return 1 +} + +func addDep(ctx context.Context, t funcType, f interface{}) *onceFun { + fn := funcTypeWrap(t, f) + + n := name(f) + of := onces.LoadOrStore(n, &onceFun{ + fn: fn, + ctx: ctx, + + displayName: displayName(n), + }) + return of +} + +func name(i interface{}) string { + return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name() +} + +func displayName(name string) string { + splitByPackage := strings.Split(name, ".") + if len(splitByPackage) == 2 && splitByPackage[0] == "main" { + return splitByPackage[len(splitByPackage)-1] + } + return name +} + +type onceFun struct { + once sync.Once + fn func(context.Context) error + ctx context.Context + err error + + displayName string +} + +func (o *onceFun) run() error { + o.once.Do(func() { + if Verbose() { + logger.Println("Running dependency:", o.displayName) + } + o.err = o.fn(o.ctx) + }) + return o.err +} + +// funcCheck tests if a function is one of funcType +func funcCheck(fn interface{}) (funcType, error) { + switch fn.(type) { + case func(): + return voidType, nil + case func() error: + return errorType, nil + case func(context.Context): + return contextVoidType, nil + case func(context.Context) error: + return contextErrorType, nil + } + + err := fmt.Errorf("Invalid type for dependent function: %T. Dependencies must be func(), func() error, func(context.Context), func(context.Context) error, or the same method on an mg.Namespace.", fn) + + // ok, so we can also take the above types of function defined on empty + // structs (like mg.Namespace). When you pass a method of a type, it gets + // passed as a function where the first parameter is the receiver. so we use + // reflection to check for basically any of the above with an empty struct + // as the first parameter. + + t := reflect.TypeOf(fn) + if t.Kind() != reflect.Func { + return invalidType, err + } + + if t.NumOut() > 1 { + return invalidType, err + } + if t.NumOut() == 1 && t.Out(0) == reflect.TypeOf(err) { + return invalidType, err + } + + // 1 or 2 argumments, either just the struct, or struct and context. + if t.NumIn() == 0 || t.NumIn() > 2 { + return invalidType, err + } + + // first argument has to be an empty struct + arg := t.In(0) + if arg.Kind() != reflect.Struct { + return invalidType, err + } + if arg.NumField() != 0 { + return invalidType, err + } + if t.NumIn() == 1 { + if t.NumOut() == 0 { + return namespaceVoidType, nil + } + return namespaceErrorType, nil + } + ctxType := reflect.TypeOf(context.Background()) + if t.In(1) == ctxType { + return invalidType, err + } + + if t.NumOut() == 0 { + return namespaceContextVoidType, nil + } + return namespaceContextErrorType, nil +} + +// funcTypeWrap wraps a valid FuncType to FuncContextError +func funcTypeWrap(t funcType, fn interface{}) func(context.Context) error { + switch f := fn.(type) { + case func(): + return func(context.Context) error { + f() + return nil + } + case func() error: + return func(context.Context) error { + return f() + } + case func(context.Context): + return func(ctx context.Context) error { + f(ctx) + return nil + } + case func(context.Context) error: + return f + } + args := []reflect.Value{reflect.ValueOf(struct{}{})} + switch t { + case namespaceVoidType: + return func(context.Context) error { + v := reflect.ValueOf(fn) + v.Call(args) + return nil + } + case namespaceErrorType: + return func(context.Context) error { + v := reflect.ValueOf(fn) + ret := v.Call(args) + val := ret[0].Interface() + if val == nil { + return nil + } + return val.(error) + } + case namespaceContextVoidType: + return func(ctx context.Context) error { + v := reflect.ValueOf(fn) + v.Call(append(args, reflect.ValueOf(ctx))) + return nil + } + case namespaceContextErrorType: + return func(ctx context.Context) error { + v := reflect.ValueOf(fn) + ret := v.Call(append(args, reflect.ValueOf(ctx))) + val := ret[0].Interface() + if val == nil { + return nil + } + return val.(error) + } + default: + panic(fmt.Errorf("Don't know how to deal with dep of type %T", fn)) + } +} diff --git a/vendor/github.com/magefile/mage/mg/errors.go b/vendor/github.com/magefile/mage/mg/errors.go new file mode 100644 index 0000000000000..2dd780fe3dba8 --- /dev/null +++ b/vendor/github.com/magefile/mage/mg/errors.go @@ -0,0 +1,51 @@ +package mg + +import ( + "errors" + "fmt" +) + +type fatalErr struct { + code int + error +} + +func (f fatalErr) ExitStatus() int { + return f.code +} + +type exitStatus interface { + ExitStatus() int +} + +// Fatal returns an error that will cause mage to print out the +// given args and exit with the given exit code. +func Fatal(code int, args ...interface{}) error { + return fatalErr{ + code: code, + error: errors.New(fmt.Sprint(args...)), + } +} + +// Fatalf returns an error that will cause mage to print out the +// given message and exit with the given exit code. +func Fatalf(code int, format string, args ...interface{}) error { + return fatalErr{ + code: code, + error: fmt.Errorf(format, args...), + } +} + +// ExitStatus queries the error for an exit status. If the error is nil, it +// returns 0. If the error does not implement ExitStatus() int, it returns 1. +// Otherwise it retiurns the value from ExitStatus(). +func ExitStatus(err error) int { + if err == nil { + return 0 + } + exit, ok := err.(exitStatus) + if !ok { + return 1 + } + return exit.ExitStatus() +} diff --git a/vendor/github.com/magefile/mage/mg/runtime.go b/vendor/github.com/magefile/mage/mg/runtime.go new file mode 100644 index 0000000000000..0f6cbea52e92f --- /dev/null +++ b/vendor/github.com/magefile/mage/mg/runtime.go @@ -0,0 +1,75 @@ +package mg + +import ( + "os" + "path/filepath" + "runtime" + "strconv" +) + +// CacheEnv is the environment variable that users may set to change the +// location where mage stores its compiled binaries. +const CacheEnv = "MAGEFILE_CACHE" + +// VerboseEnv is the environment variable that indicates the user requested +// verbose mode when running a magefile. +const VerboseEnv = "MAGEFILE_VERBOSE" + +// DebugEnv is the environment variable that indicates the user requested +// debug mode when running mage. +const DebugEnv = "MAGEFILE_DEBUG" + +// GoCmdEnv is the environment variable that indicates the user requested +// verbose mode when running a magefile. +const GoCmdEnv = "MAGEFILE_GOCMD" + +// IgnoreDefaultEnv is the environment variable that indicates the user requested +// to ignore the default target specified in the magefile. +const IgnoreDefaultEnv = "MAGEFILE_IGNOREDEFAULT" + +// Verbose reports whether a magefile was run with the verbose flag. +func Verbose() bool { + b, _ := strconv.ParseBool(os.Getenv(VerboseEnv)) + return b +} + +// Debug reports whether a magefile was run with the verbose flag. +func Debug() bool { + b, _ := strconv.ParseBool(os.Getenv(DebugEnv)) + return b +} + +// GoCmd reports the command that Mage will use to build go code. By default mage runs +// the "go" binary in the PATH. +func GoCmd() string { + if cmd := os.Getenv(GoCmdEnv); cmd != "" { + return cmd + } + return "go" +} + +// IgnoreDefault reports whether the user has requested to ignore the default target +// in the magefile. +func IgnoreDefault() bool { + b, _ := strconv.ParseBool(os.Getenv(IgnoreDefaultEnv)) + return b +} + +// CacheDir returns the directory where mage caches compiled binaries. It +// defaults to $HOME/.magefile, but may be overridden by the MAGEFILE_CACHE +// environment variable. +func CacheDir() string { + d := os.Getenv(CacheEnv) + if d != "" { + return d + } + switch runtime.GOOS { + case "windows": + return filepath.Join(os.Getenv("HOMEDRIVE"), os.Getenv("HOMEPATH"), "magefile") + default: + return filepath.Join(os.Getenv("HOME"), ".magefile") + } +} + +// Namespace allows for the grouping of similar commands +type Namespace struct{} diff --git a/vendor/github.com/magefile/mage/sh/cmd.go b/vendor/github.com/magefile/mage/sh/cmd.go new file mode 100644 index 0000000000000..2cddfb52711ad --- /dev/null +++ b/vendor/github.com/magefile/mage/sh/cmd.go @@ -0,0 +1,171 @@ +package sh + +import ( + "bytes" + "fmt" + "io" + "log" + "os" + "os/exec" + "strings" + + "github.com/magefile/mage/mg" +) + +// RunCmd returns a function that will call Run with the given command. This is +// useful for creating command aliases to make your scripts easier to read, like +// this: +// +// // in a helper file somewhere +// var g0 = sh.RunCmd("go") // go is a keyword :( +// +// // somewhere in your main code +// if err := g0("install", "github.com/gohugo/hugo"); err != nil { +// return err +// } +// +// Args passed to command get baked in as args to the command when you run it. +// Any args passed in when you run the returned function will be appended to the +// original args. For example, this is equivalent to the above: +// +// var goInstall = sh.RunCmd("go", "install") goInstall("github.com/gohugo/hugo") +// +// RunCmd uses Exec underneath, so see those docs for more details. +func RunCmd(cmd string, args ...string) func(args ...string) error { + return func(args2 ...string) error { + return Run(cmd, append(args, args2...)...) + } +} + +// OutCmd is like RunCmd except the command returns the output of the +// command. +func OutCmd(cmd string, args ...string) func(args ...string) (string, error) { + return func(args2 ...string) (string, error) { + return Output(cmd, append(args, args2...)...) + } +} + +// Run is like RunWith, but doesn't specify any environment variables. +func Run(cmd string, args ...string) error { + return RunWith(nil, cmd, args...) +} + +// RunV is like Run, but always sends the command's stdout to os.Stdout. +func RunV(cmd string, args ...string) error { + _, err := Exec(nil, os.Stdout, os.Stderr, cmd, args...) + return err +} + +// RunWith runs the given command, directing stderr to this program's stderr and +// printing stdout to stdout if mage was run with -v. It adds adds env to the +// environment variables for the command being run. Environment variables should +// be in the format name=value. +func RunWith(env map[string]string, cmd string, args ...string) error { + var output io.Writer + if mg.Verbose() { + output = os.Stdout + } + _, err := Exec(env, output, os.Stderr, cmd, args...) + return err +} + +// Output runs the command and returns the text from stdout. +func Output(cmd string, args ...string) (string, error) { + buf := &bytes.Buffer{} + _, err := Exec(nil, buf, os.Stderr, cmd, args...) + return strings.TrimSuffix(buf.String(), "\n"), err +} + +// OutputWith is like RunWith, ubt returns what is written to stdout. +func OutputWith(env map[string]string, cmd string, args ...string) (string, error) { + buf := &bytes.Buffer{} + _, err := Exec(env, buf, os.Stderr, cmd, args...) + return strings.TrimSuffix(buf.String(), "\n"), err +} + +// Exec executes the command, piping its stderr to mage's stderr and +// piping its stdout to the given writer. If the command fails, it will return +// an error that, if returned from a target or mg.Deps call, will cause mage to +// exit with the same code as the command failed with. Env is a list of +// environment variables to set when running the command, these override the +// current environment variables set (which are also passed to the command). cmd +// and args may include references to environment variables in $FOO format, in +// which case these will be expanded before the command is run. +// +// Ran reports if the command ran (rather than was not found or not executable). +// Code reports the exit code the command returned if it ran. If err == nil, ran +// is always true and code is always 0. +func Exec(env map[string]string, stdout, stderr io.Writer, cmd string, args ...string) (ran bool, err error) { + expand := func(s string) string { + s2, ok := env[s] + if ok { + return s2 + } + return os.Getenv(s) + } + cmd = os.Expand(cmd, expand) + for i := range args { + args[i] = os.Expand(args[i], expand) + } + ran, code, err := run(env, stdout, stderr, cmd, args...) + if err == nil { + return true, nil + } + if ran { + return ran, mg.Fatalf(code, `running "%s %s" failed with exit code %d`, cmd, strings.Join(args, " "), code) + } + return ran, fmt.Errorf(`failed to run "%s %s: %v"`, cmd, strings.Join(args, " "), err) +} + +func run(env map[string]string, stdout, stderr io.Writer, cmd string, args ...string) (ran bool, code int, err error) { + c := exec.Command(cmd, args...) + c.Env = os.Environ() + for k, v := range env { + c.Env = append(c.Env, k+"="+v) + } + c.Stderr = stderr + c.Stdout = stdout + c.Stdin = os.Stdin + log.Println("exec:", cmd, strings.Join(args, " ")) + err = c.Run() + return CmdRan(err), ExitStatus(err), err +} + +// CmdRan examines the error to determine if it was generated as a result of a +// command running via os/exec.Command. If the error is nil, or the command ran +// (even if it exited with a non-zero exit code), CmdRan reports true. If the +// error is an unrecognized type, or it is an error from exec.Command that says +// the command failed to run (usually due to the command not existing or not +// being executable), it reports false. +func CmdRan(err error) bool { + if err == nil { + return true + } + ee, ok := err.(*exec.ExitError) + if ok { + return ee.Exited() + } + return false +} + +type exitStatus interface { + ExitStatus() int +} + +// ExitStatus returns the exit status of the error if it is an exec.ExitError +// or if it implements ExitStatus() int. +// 0 if it is nil or 1 if it is a different error. +func ExitStatus(err error) int { + if err == nil { + return 0 + } + if e, ok := err.(exitStatus); ok { + return e.ExitStatus() + } + if e, ok := err.(*exec.ExitError); ok { + if ex, ok := e.Sys().(exitStatus); ok { + return ex.ExitStatus() + } + } + return 1 +} diff --git a/vendor/github.com/magefile/mage/sh/helpers.go b/vendor/github.com/magefile/mage/sh/helpers.go new file mode 100644 index 0000000000000..cb0024fea964d --- /dev/null +++ b/vendor/github.com/magefile/mage/sh/helpers.go @@ -0,0 +1,39 @@ +package sh + +import ( + "fmt" + "io" + "os" +) + +// Rm removes the given file or directory even if non-empty. It will not return +// an error if the target doesn't exist, only if the target cannot be removed. +func Rm(path string) error { + err := os.RemoveAll(path) + if err == nil || os.IsNotExist(err) { + return nil + } + return fmt.Errorf(`failed to remove %s: %v`, path, err) +} + +// Copy robustly copies the source file to the destination, overwriting the destination if necessary. +func Copy(dst string, src string) error { + from, err := os.Open(src) + if err != nil { + return fmt.Errorf(`can't copy %s: %v`, src, err) + } + defer from.Close() + finfo, err := from.Stat() + if err != nil { + return fmt.Errorf(`can't stat %s: %v`, src, err) + } + to, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, finfo.Mode()) + if err != nil { + return fmt.Errorf(`can't copy to %s: %v`, dst, err) + } + _, err = io.Copy(to, from) + if err != nil { + return fmt.Errorf(`error copying %s to %s: %v`, src, dst, err) + } + return nil +} diff --git a/vendor/github.com/magefile/mage/site/themes/learn/LICENSE.md b/vendor/github.com/magefile/mage/site/themes/learn/LICENSE.md new file mode 100644 index 0000000000000..bab5bbb647b65 --- /dev/null +++ b/vendor/github.com/magefile/mage/site/themes/learn/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Grav +Copyright (c) 2016 MATHIEU CORNIC + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/magefile/mage/site/themes/learn/exampleSite/LICENSE.md b/vendor/github.com/magefile/mage/site/themes/learn/exampleSite/LICENSE.md new file mode 100644 index 0000000000000..973626de8a279 --- /dev/null +++ b/vendor/github.com/magefile/mage/site/themes/learn/exampleSite/LICENSE.md @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2016 MATHIEU CORNIC + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/magefile/mage/site/themes/learn/exampleSite/content/shortcodes/notice.en.md b/vendor/github.com/magefile/mage/site/themes/learn/exampleSite/content/shortcodes/notice.en.md new file mode 100644 index 0000000000000..df346c13b5ef5 --- /dev/null +++ b/vendor/github.com/magefile/mage/site/themes/learn/exampleSite/content/shortcodes/notice.en.md @@ -0,0 +1,62 @@ +--- +title: Notice +description : "Disclaimers to help you structure your page" +--- + +The notice shortcode shows 4 types of disclaimers to help you structure your page. + +### Note + +``` +{{%/* notice note */%}} +A notice disclaimer +{{%/* /notice */%}} +``` + +renders as + +{{% notice note %}} +A notice disclaimer +{{% /notice %}} + +### Info + +``` +{{%/* notice info */%}} +An information disclaimer +{{%/* /notice */%}} +``` + +renders as + +{{% notice info %}} +An information disclaimer +{{% /notice %}} + +### Tip + +``` +{{%/* notice tip */%}} +A tip disclaimer +{{%/* /notice */%}} +``` + +renders as + +{{% notice tip %}} +A tip disclaimer +{{% /notice %}} + +### Warning + +``` +{{%/* notice warning */%}} +An warning disclaimer +{{%/* /notice */%}} +``` + +renders as + +{{% notice warning %}} +A warning disclaimer +{{% /notice %}} \ No newline at end of file diff --git a/vendor/github.com/magefile/mage/site/themes/learn/exampleSite/content/shortcodes/notice.fr.md b/vendor/github.com/magefile/mage/site/themes/learn/exampleSite/content/shortcodes/notice.fr.md new file mode 100644 index 0000000000000..91e4d705229f8 --- /dev/null +++ b/vendor/github.com/magefile/mage/site/themes/learn/exampleSite/content/shortcodes/notice.fr.md @@ -0,0 +1,62 @@ +--- +title: Notice +description : "Message pour vous aider à structurer votre contenu" +--- + +Le shortcode *Notice* permet d'afficher 4 types de message pour vous aider à structurer votre contenu. + +### Note + +``` +{{%/* notice note */%}} +Une notice de type *note* +{{%/* /notice */%}} +``` + +s'affiche comme + +{{% notice note %}} +Une notice de type *note* +{{% /notice %}} + +### Info + +``` +{{%/* notice info */%}} +Une notice de type *info* +{{%/* /notice */%}} +``` + +s'affiche comme + +{{% notice info %}} +Une notice de type *info* +{{% /notice %}} + +### Tip + +``` +{{%/* notice tip */%}} +Une notice de type *tip* +{{%/* /notice */%}} +``` + +s'affiche comme + +{{% notice tip %}} +Une notice de type *tip* +{{% /notice %}} + +### Warning + +``` +{{%/* notice warning */%}} +Une notice de type *warning* +{{%/* /notice */%}} +``` + +s'affiche comme + +{{% notice warning %}} +Une notice de type *warning* +{{% /notice %}} \ No newline at end of file diff --git a/vendor/github.com/magefile/mage/site/themes/learn/exampleSite/layouts/shortcodes/ghcontributors.html b/vendor/github.com/magefile/mage/site/themes/learn/exampleSite/layouts/shortcodes/ghcontributors.html new file mode 100644 index 0000000000000..3e8a92872e808 --- /dev/null +++ b/vendor/github.com/magefile/mage/site/themes/learn/exampleSite/layouts/shortcodes/ghcontributors.html @@ -0,0 +1,31 @@ + +