mirror of
https://github.com/crazy-max/diun.git
synced 2025-12-21 21:33:22 +01:00
Add CLI to interact with Diun through gRPC (#382)
Add simple CLI to interact with Diun through gRPC Create image and notif proto services Compile and validate protos through a dedicated Dockerfile and bake target Implement proto definitions Move server as `serve` command New commands `image` and `notif` Refactor command line usage doc Better CLI error handling Tools build constraint to manage tools deps through go modules Add upgrade notes Co-authored-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
19
.github/workflows/build.yml
vendored
19
.github/workflows/build.yml
vendored
@@ -25,6 +25,21 @@ env:
|
|||||||
GHCR_SLUG: ghcr.io/crazy-max/diun
|
GHCR_SLUG: ghcr.io/crazy-max/diun
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
validate:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
-
|
||||||
|
name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
-
|
||||||
|
name: Validate
|
||||||
|
uses: docker/bake-action@v1
|
||||||
|
with:
|
||||||
|
targets: validate
|
||||||
|
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -49,7 +64,9 @@ jobs:
|
|||||||
|
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [ test ]
|
needs:
|
||||||
|
- validate
|
||||||
|
- test
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
|
|||||||
28
.github/workflows/validate.yml
vendored
28
.github/workflows/validate.yml
vendored
@@ -1,28 +0,0 @@
|
|||||||
name: validate
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'master'
|
|
||||||
tags:
|
|
||||||
- 'v*'
|
|
||||||
- 'dockerfile/*'
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- 'master'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
validate:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
-
|
|
||||||
name: Validate
|
|
||||||
uses: docker/bake-action@v1
|
|
||||||
with:
|
|
||||||
targets: validate
|
|
||||||
@@ -18,7 +18,7 @@ RUN --mount=type=bind,target=/src,rw \
|
|||||||
--dist "/out" \
|
--dist "/out" \
|
||||||
--hooks="go mod tidy" \
|
--hooks="go mod tidy" \
|
||||||
--hooks="go mod download" \
|
--hooks="go mod download" \
|
||||||
--main="./cmd/main.go" \
|
--main="./cmd" \
|
||||||
--ldflags="-s -w -X 'main.version={{.Version}}'" \
|
--ldflags="-s -w -X 'main.version={{.Version}}'" \
|
||||||
--files="CHANGELOG.md" \
|
--files="CHANGELOG.md" \
|
||||||
--files="LICENSE" \
|
--files="LICENSE" \
|
||||||
@@ -44,3 +44,4 @@ ENV PROFILER_PATH="/profiler" \
|
|||||||
|
|
||||||
VOLUME [ "/data" ]
|
VOLUME [ "/data" ]
|
||||||
ENTRYPOINT [ "diun" ]
|
ENTRYPOINT [ "diun" ]
|
||||||
|
CMD [ "serve" ]
|
||||||
|
|||||||
34
cmd/cli.go
Normal file
34
cmd/cli.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/crazy-max/diun/v4/pb"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CliHandler is a cli interface
|
||||||
|
type CliHandler interface {
|
||||||
|
BeforeApply() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// CliGlobals holds globals cli attributes
|
||||||
|
type CliGlobals struct {
|
||||||
|
CliHandler `kong:"-"`
|
||||||
|
|
||||||
|
conn *grpc.ClientConn `kong:"-"`
|
||||||
|
imageSvc pb.ImageServiceClient `kong:"-"`
|
||||||
|
notifSvc pb.NotifServiceClient `kong:"-"`
|
||||||
|
|
||||||
|
GRPCAuthority string `kong:"name='grpc-authority',default='127.0.0.1:42286',help='Link to Diun gRPC API.'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeApply is a hook that run cli cmd are executed.
|
||||||
|
func (s *CliGlobals) BeforeApply() (err error) {
|
||||||
|
s.conn, err = grpc.Dial(s.GRPCAuthority, grpc.WithInsecure())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.imageSvc = pb.NewImageServiceClient(s.conn)
|
||||||
|
s.notifSvc = pb.NewNotifServiceClient(s.conn)
|
||||||
|
return
|
||||||
|
}
|
||||||
131
cmd/image.go
Normal file
131
cmd/image.go
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/crazy-max/diun/v4/pb"
|
||||||
|
"github.com/docker/go-units"
|
||||||
|
"github.com/jedib0t/go-pretty/v6/table"
|
||||||
|
"github.com/tidwall/pretty"
|
||||||
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ImageCmd holds image command
|
||||||
|
type ImageCmd struct {
|
||||||
|
List ImageListCmd `kong:"cmd,default='1',help='List images in database.'"`
|
||||||
|
Inspect ImageInspectCmd `kong:"cmd,help='Display information of an image in database.'"`
|
||||||
|
Remove ImageRemoveCmd `kong:"cmd,help='Remove an image manifest from database.'"`
|
||||||
|
//Prune ImagePruneCmd `kong:"cmd,help='Remove unused manifests from the database.'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageListCmd holds image list command
|
||||||
|
type ImageListCmd struct {
|
||||||
|
CliGlobals
|
||||||
|
Raw bool `kong:"name='raw',default='false',help='JSON output.'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ImageListCmd) Run(ctx *Context) error {
|
||||||
|
defer s.conn.Close()
|
||||||
|
|
||||||
|
il, err := s.imageSvc.ImageList(context.Background(), &pb.ImageListRequest{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(il.Images, func(i, j int) bool {
|
||||||
|
return strings.Map(unicode.ToUpper, il.Images[i].Name) < strings.Map(unicode.ToUpper, il.Images[j].Name)
|
||||||
|
})
|
||||||
|
|
||||||
|
if s.Raw {
|
||||||
|
b, _ := protojson.Marshal(il)
|
||||||
|
fmt.Println(string(pretty.Pretty(b)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
t := table.NewWriter()
|
||||||
|
t.SetOutputMirror(os.Stdout)
|
||||||
|
t.AppendHeader(table.Row{"Name", "Manifests Count", "Latest Tag", "Latest Created", "Latest Digest"})
|
||||||
|
for _, image := range il.Images {
|
||||||
|
t.AppendRow(table.Row{image.Name, image.ManifestsCount, image.Latest.Tag, image.Latest.Created.AsTime().Format(time.RFC3339), image.Latest.Digest})
|
||||||
|
}
|
||||||
|
t.AppendFooter(table.Row{"Total", len(il.Images)})
|
||||||
|
t.Render()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageInspectCmd holds image inspect command
|
||||||
|
type ImageInspectCmd struct {
|
||||||
|
CliGlobals
|
||||||
|
Image string `kong:"name='image',required,help='Image to inspect.'"`
|
||||||
|
Raw bool `kong:"name='raw',default='false',help='JSON output.'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ImageInspectCmd) Run(ctx *Context) error {
|
||||||
|
defer s.conn.Close()
|
||||||
|
|
||||||
|
ii, err := s.imageSvc.ImageInspect(context.Background(), &pb.ImageInspectRequest{
|
||||||
|
Name: s.Image,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(ii.Image.Manifests, func(i, j int) bool {
|
||||||
|
return ii.Image.Manifests[i].Created.AsTime().After(ii.Image.Manifests[j].Created.AsTime())
|
||||||
|
})
|
||||||
|
|
||||||
|
if s.Raw {
|
||||||
|
b, _ := protojson.Marshal(ii)
|
||||||
|
fmt.Println(string(pretty.Pretty(b)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
t := table.NewWriter()
|
||||||
|
t.SetOutputMirror(os.Stdout)
|
||||||
|
t.AppendHeader(table.Row{"Tag", "Created", "Digest"})
|
||||||
|
for _, image := range ii.Image.Manifests {
|
||||||
|
t.AppendRow(table.Row{image.Tag, image.Created.AsTime().Format(time.RFC3339), image.Digest})
|
||||||
|
}
|
||||||
|
t.AppendFooter(table.Row{"Total", len(ii.Image.Manifests)})
|
||||||
|
t.Render()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageRemoveCmd holds image remove command
|
||||||
|
type ImageRemoveCmd struct {
|
||||||
|
CliGlobals
|
||||||
|
Image string `kong:"name='image',required,help='Image to remove.'"`
|
||||||
|
All bool `kong:"name='all',default='false',help='Remove all manifests from the database.'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ImageRemoveCmd) Run(ctx *Context) error {
|
||||||
|
defer s.conn.Close()
|
||||||
|
|
||||||
|
removed, err := s.imageSvc.ImageRemove(context.Background(), &pb.ImageRemoveRequest{
|
||||||
|
Name: s.Image,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
t := table.NewWriter()
|
||||||
|
t.SetOutputMirror(os.Stdout)
|
||||||
|
t.AppendHeader(table.Row{"Tag", "Created", "Digest", "Size"})
|
||||||
|
var totalSize int64
|
||||||
|
for _, image := range removed.Manifests {
|
||||||
|
t.AppendRow(table.Row{image.Tag, image.Created.AsTime().Format(time.RFC3339), image.Digest, units.HumanSize(float64(image.Size))})
|
||||||
|
totalSize += image.Size
|
||||||
|
}
|
||||||
|
t.AppendFooter(table.Row{"Total", fmt.Sprintf("%d (%s)", len(removed.Manifests), units.HumanSize(float64(totalSize)))})
|
||||||
|
t.Render()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
104
cmd/main.go
104
cmd/main.go
@@ -3,48 +3,48 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
|
||||||
"path"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
_ "time/tzdata"
|
_ "time/tzdata"
|
||||||
|
|
||||||
"github.com/alecthomas/kong"
|
"github.com/alecthomas/kong"
|
||||||
"github.com/crazy-max/diun/v4/internal/app"
|
|
||||||
"github.com/crazy-max/diun/v4/internal/config"
|
|
||||||
"github.com/crazy-max/diun/v4/internal/logging"
|
|
||||||
"github.com/crazy-max/diun/v4/internal/model"
|
"github.com/crazy-max/diun/v4/internal/model"
|
||||||
"github.com/pkg/profile"
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
diun *app.Diun
|
|
||||||
cli model.Cli
|
|
||||||
version = "dev"
|
version = "dev"
|
||||||
meta = model.Meta{
|
cli struct {
|
||||||
|
Version kong.VersionFlag
|
||||||
|
Serve ServeCmd `kong:"cmd,help='Starts Diun server.'"`
|
||||||
|
Image ImageCmd `kong:"cmd,help='Manage image manifests.'"`
|
||||||
|
Notif NotifCmd `kong:"cmd,help='Manage notifications.'"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type Context struct {
|
||||||
|
Meta model.Meta
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var err error
|
||||||
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
|
||||||
|
meta := model.Meta{
|
||||||
ID: "diun",
|
ID: "diun",
|
||||||
Name: "Diun",
|
Name: "Diun",
|
||||||
Desc: "Docker image update notifier",
|
Desc: "Docker image update notifier",
|
||||||
URL: "https://github.com/crazy-max/diun",
|
URL: "https://github.com/crazy-max/diun",
|
||||||
Logo: "https://raw.githubusercontent.com/crazy-max/diun/master/.res/diun.png",
|
Logo: "https://raw.githubusercontent.com/crazy-max/diun/master/.res/diun.png",
|
||||||
Author: "CrazyMax",
|
Author: "CrazyMax",
|
||||||
|
Version: version,
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var err error
|
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
|
||||||
|
|
||||||
meta.Version = version
|
|
||||||
meta.UserAgent = fmt.Sprintf("%s/%s go/%s %s", meta.ID, meta.Version, runtime.Version()[2:], strings.Title(runtime.GOOS))
|
meta.UserAgent = fmt.Sprintf("%s/%s go/%s %s", meta.ID, meta.Version, runtime.Version()[2:], strings.Title(runtime.GOOS))
|
||||||
if meta.Hostname, err = os.Hostname(); err != nil {
|
if meta.Hostname, err = os.Hostname(); err != nil {
|
||||||
log.Fatal().Err(err).Msg("Cannot resolve hostname")
|
log.Fatal().Err(err).Msg("Cannot resolve hostname")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse command line
|
ctx := kong.Parse(&cli,
|
||||||
_ = kong.Parse(&cli,
|
|
||||||
kong.Name(meta.ID),
|
kong.Name(meta.ID),
|
||||||
kong.Description(fmt.Sprintf("%s. More info: %s", meta.Desc, meta.URL)),
|
kong.Description(fmt.Sprintf("%s. More info: %s", meta.Desc, meta.URL)),
|
||||||
kong.UsageOnError(),
|
kong.UsageOnError(),
|
||||||
@@ -56,69 +56,5 @@ func main() {
|
|||||||
Summary: true,
|
Summary: true,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Init
|
ctx.FatalIfErrorf(ctx.Run(&Context{Meta: meta}))
|
||||||
logging.Configure(&cli)
|
|
||||||
log.Info().Str("version", version).Msgf("Starting %s", meta.Name)
|
|
||||||
|
|
||||||
// Handle os signals
|
|
||||||
channel := make(chan os.Signal)
|
|
||||||
signal.Notify(channel, os.Interrupt, syscall.SIGTERM)
|
|
||||||
go func() {
|
|
||||||
sig := <-channel
|
|
||||||
diun.Close()
|
|
||||||
log.Warn().Msgf("Caught signal %v", sig)
|
|
||||||
os.Exit(0)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Load configuration
|
|
||||||
cfg, err := config.Load(cli)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Cannot load configuration")
|
|
||||||
}
|
|
||||||
log.Debug().Msg(cfg.String())
|
|
||||||
|
|
||||||
// Profiler
|
|
||||||
if len(cli.Profiler) > 0 && len(cli.ProfilerPath) > 0 {
|
|
||||||
profilerPath := path.Clean(cli.ProfilerPath)
|
|
||||||
if err = os.MkdirAll(profilerPath, os.ModePerm); err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Cannot create profiler folder")
|
|
||||||
}
|
|
||||||
profilePath := profile.ProfilePath(profilerPath)
|
|
||||||
switch cli.Profiler {
|
|
||||||
case "cpu":
|
|
||||||
defer profile.Start(profile.CPUProfile, profilePath).Stop()
|
|
||||||
case "mem":
|
|
||||||
defer profile.Start(profile.MemProfile, profilePath).Stop()
|
|
||||||
case "alloc":
|
|
||||||
defer profile.Start(profile.MemProfileAllocs, profilePath).Stop()
|
|
||||||
case "heap":
|
|
||||||
defer profile.Start(profile.MemProfileHeap, profilePath).Stop()
|
|
||||||
case "routines":
|
|
||||||
defer profile.Start(profile.GoroutineProfile, profilePath).Stop()
|
|
||||||
case "mutex":
|
|
||||||
defer profile.Start(profile.MutexProfile, profilePath).Stop()
|
|
||||||
case "threads":
|
|
||||||
defer profile.Start(profile.ThreadcreationProfile, profilePath).Stop()
|
|
||||||
case "block":
|
|
||||||
defer profile.Start(profile.BlockProfile, profilePath).Stop()
|
|
||||||
default:
|
|
||||||
log.Fatal().Msgf("Unknown profiler: %s", cli.Profiler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init
|
|
||||||
if diun, err = app.New(meta, cli, cfg); err != nil {
|
|
||||||
log.Fatal().Err(err).Msgf("Cannot initialize %s", meta.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test notif
|
|
||||||
if cli.TestNotif {
|
|
||||||
diun.TestNotif()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start
|
|
||||||
if err = diun.Start(); err != nil {
|
|
||||||
log.Fatal().Err(err).Msgf("Cannot start %s", meta.Name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
30
cmd/notif.go
Normal file
30
cmd/notif.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/crazy-max/diun/v4/pb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NotifCmd holds notif command
|
||||||
|
type NotifCmd struct {
|
||||||
|
Test NotifTestCmd `kong:"cmd,help='Test notification settings.'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifTestCmd holds notif test command
|
||||||
|
type NotifTestCmd struct {
|
||||||
|
CliGlobals
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NotifTestCmd) Run(ctx *Context) error {
|
||||||
|
defer s.conn.Close()
|
||||||
|
|
||||||
|
nt, err := s.notifSvc.NotifTest(context.Background(), &pb.NotifTestRequest{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(nt.Message)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
99
cmd/serve.go
Normal file
99
cmd/serve.go
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"path"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/crazy-max/diun/v4/internal/app"
|
||||||
|
"github.com/crazy-max/diun/v4/internal/config"
|
||||||
|
"github.com/crazy-max/diun/v4/internal/logging"
|
||||||
|
"github.com/pkg/profile"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServeCmd holds serve command args and flags
|
||||||
|
type ServeCmd struct {
|
||||||
|
Cfgfile string `kong:"name='config',env='CONFIG',help='Diun configuration file.'"`
|
||||||
|
ProfilerPath string `kong:"name='profiler-path',env='PROFILER_PATH',help='Base path where profiling files are written.'"`
|
||||||
|
Profiler string `kong:"name='profiler',env='PROFILER',help='Profiler to use.'"`
|
||||||
|
LogLevel string `kong:"name='log-level',env='LOG_LEVEL',default='info',help='Set log level.'"`
|
||||||
|
LogJSON bool `kong:"name='log-json',env='LOG_JSON',default='false',help='Enable JSON logging output.'"`
|
||||||
|
LogCaller bool `kong:"name='log-caller',env='LOG_CALLER',default='false',help='Add file:line of the caller to log output.'"`
|
||||||
|
LogNoColor bool `kong:"name='log-nocolor',env='LOG_NOCOLOR',default='false',help='Disables the colorized output.'"`
|
||||||
|
GRPCAuthority string `kong:"name='grpc-authority',env='GRPC_AUTHORITY',default=':42286',help='Address used to expose the gRPC server.'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServeCmd) Run(ctx *Context) error {
|
||||||
|
var diun *app.Diun
|
||||||
|
|
||||||
|
// Logging
|
||||||
|
logging.Configure(logging.Options{
|
||||||
|
LogLevel: s.LogLevel,
|
||||||
|
LogJSON: s.LogJSON,
|
||||||
|
LogCaller: s.LogCaller,
|
||||||
|
LogNoColor: s.LogNoColor,
|
||||||
|
})
|
||||||
|
log.Info().Str("version", version).Msgf("Starting %s", ctx.Meta.Name)
|
||||||
|
|
||||||
|
// Handle os signals
|
||||||
|
channel := make(chan os.Signal)
|
||||||
|
signal.Notify(channel, os.Interrupt, syscall.SIGTERM)
|
||||||
|
go func() {
|
||||||
|
sig := <-channel
|
||||||
|
diun.Close()
|
||||||
|
log.Warn().Msgf("Caught signal %v", sig)
|
||||||
|
os.Exit(0)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Load configuration
|
||||||
|
cfg, err := config.Load(s.Cfgfile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Cannot load configuration")
|
||||||
|
}
|
||||||
|
log.Debug().Msg(cfg.String())
|
||||||
|
|
||||||
|
// Profiler
|
||||||
|
if len(s.Profiler) > 0 && len(s.ProfilerPath) > 0 {
|
||||||
|
profilerPath := path.Clean(s.ProfilerPath)
|
||||||
|
if err = os.MkdirAll(profilerPath, os.ModePerm); err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Cannot create profiler folder")
|
||||||
|
}
|
||||||
|
profilePath := profile.ProfilePath(profilerPath)
|
||||||
|
switch s.Profiler {
|
||||||
|
case "cpu":
|
||||||
|
defer profile.Start(profile.CPUProfile, profilePath).Stop()
|
||||||
|
case "mem":
|
||||||
|
defer profile.Start(profile.MemProfile, profilePath).Stop()
|
||||||
|
case "alloc":
|
||||||
|
defer profile.Start(profile.MemProfileAllocs, profilePath).Stop()
|
||||||
|
case "heap":
|
||||||
|
defer profile.Start(profile.MemProfileHeap, profilePath).Stop()
|
||||||
|
case "routines":
|
||||||
|
defer profile.Start(profile.GoroutineProfile, profilePath).Stop()
|
||||||
|
case "mutex":
|
||||||
|
defer profile.Start(profile.MutexProfile, profilePath).Stop()
|
||||||
|
case "threads":
|
||||||
|
defer profile.Start(profile.ThreadcreationProfile, profilePath).Stop()
|
||||||
|
case "block":
|
||||||
|
defer profile.Start(profile.BlockProfile, profilePath).Stop()
|
||||||
|
default:
|
||||||
|
log.Fatal().Msgf("Unknown profiler: %s", s.Profiler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init
|
||||||
|
diun, err = app.New(ctx.Meta, cfg, s.GRPCAuthority)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msgf("Cannot initialize %s", ctx.Meta.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start
|
||||||
|
err = diun.Start()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msgf("Cannot start %s", ctx.Meta.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -9,6 +9,17 @@ target "go-version" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// protoc version
|
||||||
|
variable "PROTOC_VERSION" {
|
||||||
|
default = "3.17.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
target "protoc-version" {
|
||||||
|
args = {
|
||||||
|
PROTOC_VERSION = PROTOC_VERSION
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GitHub reference as defined in GitHub Actions (eg. refs/head/master))
|
// GitHub reference as defined in GitHub Actions (eg. refs/head/master))
|
||||||
variable "GITHUB_REF" {
|
variable "GITHUB_REF" {
|
||||||
default = ""
|
default = ""
|
||||||
@@ -30,7 +41,7 @@ group "default" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group "validate" {
|
group "validate" {
|
||||||
targets = ["lint", "vendor-validate"]
|
targets = ["lint", "vendor-validate", "gen-validate"]
|
||||||
}
|
}
|
||||||
|
|
||||||
target "lint" {
|
target "lint" {
|
||||||
@@ -52,6 +63,19 @@ target "vendor-update" {
|
|||||||
output = ["."]
|
output = ["."]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target "gen-validate" {
|
||||||
|
inherits = ["go-version", "protoc-version"]
|
||||||
|
dockerfile = "./hack/gen.Dockerfile"
|
||||||
|
target = "validate"
|
||||||
|
}
|
||||||
|
|
||||||
|
target "gen-update" {
|
||||||
|
inherits = ["go-version", "protoc-version"]
|
||||||
|
dockerfile = "./hack/gen.Dockerfile"
|
||||||
|
target = "update"
|
||||||
|
output = ["."]
|
||||||
|
}
|
||||||
|
|
||||||
target "test" {
|
target "test" {
|
||||||
inherits = ["go-version"]
|
inherits = ["go-version"]
|
||||||
dockerfile = "./hack/test.Dockerfile"
|
dockerfile = "./hack/test.Dockerfile"
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ At startup, Diun searches for a file named `diun.yml` (or `diun.yaml`) in:
|
|||||||
* `$HOME/.config/`
|
* `$HOME/.config/`
|
||||||
* `.` _(the working directory)_
|
* `.` _(the working directory)_
|
||||||
|
|
||||||
You can override this using the [`--config` flag or `CONFIG` env var](../usage/cli.md).
|
You can override this using the [`--config` flag or `CONFIG` env var with `serve` command](../usage/command-line.md#serve).
|
||||||
|
|
||||||
??? example "diun.yml"
|
??? example "diun.yml"
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
@@ -20,13 +20,11 @@ regopts:
|
|||||||
passwordFile: /run/secrets/password
|
passwordFile: /run/secrets/password
|
||||||
```
|
```
|
||||||
|
|
||||||
`myregistry` will be used as a `name` selector (default) if referenced by its [name](#name).
|
* `myregistry` will be used as a `name` selector (default) if referenced by its [name](#name).
|
||||||
|
* `docker.io` will be used as an `image` selector. If an image is on DockerHub (`docker.io` domain), this registry
|
||||||
`docker.io` will be used as an `image` selector. If an image is on DockerHub (`docker.io` domain), this registry options will
|
options will be selected if not referenced as a `regopt` name.
|
||||||
be selected if not referenced as a `regopt` name.
|
* `docker.io/crazymax` will be used as an `image` selector. If an image is on DockerHub and in `crazymax` namespace,
|
||||||
|
this registry options will be selected if not referenced as a `regopt` name.
|
||||||
`docker.io/crazymax` will be used as an `image` selector. If an image is on DockerHub and in `crazymax` namespace, this registry options will
|
|
||||||
be selected if not referenced as a `regopt` name.
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ Send notification at the very first analysis of an image. (default `false`)
|
|||||||
### `compareDigest`
|
### `compareDigest`
|
||||||
|
|
||||||
Compare the digest of an image with the registry before downloading the image manifest. It is strongly
|
Compare the digest of an image with the registry before downloading the image manifest. It is strongly
|
||||||
recommended to leave this value at `true`, especially with [Docker Hub which imposes a rate-limit](../faq.md#docker-hub-rate-limits)
|
recommended leaving this value at `true`, especially with [Docker Hub which imposes a rate-limit](../faq.md#docker-hub-rate-limits)
|
||||||
on image pull. (default `true`)
|
on image pull. (default `true`)
|
||||||
|
|
||||||
!!! example "Config file"
|
!!! example "Config file"
|
||||||
@@ -74,7 +74,7 @@ on image pull. (default `true`)
|
|||||||
|
|
||||||
### `healthchecks`
|
### `healthchecks`
|
||||||
|
|
||||||
Healthchecks allows to monitor Diun watcher by sending start and success notification
|
Healthchecks allows monitoring Diun watcher by sending start and success notification
|
||||||
events to [healthchecks.io](https://healthchecks.io/).
|
events to [healthchecks.io](https://healthchecks.io/).
|
||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
|
|||||||
@@ -14,16 +14,16 @@ watch:
|
|||||||
|
|
||||||
## Test notifications
|
## Test notifications
|
||||||
|
|
||||||
Through the [command line](usage/cli.md) with:
|
Through the [command line](usage/command-line.md#notif-test) with:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
diun --config ./diun.yml --test-notif
|
diun notif test
|
||||||
```
|
```
|
||||||
|
|
||||||
Or within a container:
|
Or within a container:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker-compose exec diun diun --test-notif
|
docker-compose exec diun diun notif test
|
||||||
```
|
```
|
||||||
|
|
||||||
## field docker|swarm uses unsupported type: invalid
|
## field docker|swarm uses unsupported type: invalid
|
||||||
@@ -127,7 +127,8 @@ Or you can tweak the [`schedule` setting](config/watch.md#schedule) with somethi
|
|||||||
|
|
||||||
## Profiling
|
## Profiling
|
||||||
|
|
||||||
Diun provides a simple way to manage runtime/pprof profiling through [`--profiler-path` and `--profiler` flags](usage/cli.md#options):
|
Diun provides a simple way to manage runtime/pprof profiling through the
|
||||||
|
[`--profiler-path` and `--profiler` flags with `serve` command](usage/command-line.md#serve):
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: "3.5"
|
version: "3.5"
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ And extract diun:
|
|||||||
wget -qO- {{ config.repo_url }}releases/download/v{{ git.tag | trim('v') }}/diun_{{ git.tag | trim('v') }}_linux_x86_64.tar.gz | tar -zxvf - diun
|
wget -qO- {{ config.repo_url }}releases/download/v{{ git.tag | trim('v') }}/diun_{{ git.tag | trim('v') }}_linux_x86_64.tar.gz | tar -zxvf - diun
|
||||||
```
|
```
|
||||||
|
|
||||||
After getting the binary, it can be tested with [`./diun --help`](../usage/cli.md) command and moved to a permanent
|
After getting the binary, it can be tested with [`./diun --help`](../usage/command-line.md#global-options) command
|
||||||
location.
|
and moved to a permanent location.
|
||||||
|
|
||||||
## Server configuration
|
## Server configuration
|
||||||
|
|
||||||
|
|||||||
@@ -233,7 +233,8 @@ Following the transposition of the configuration into environment variables, the
|
|||||||
is no longer loaded by default in the official Docker image.
|
is no longer loaded by default in the official Docker image.
|
||||||
|
|
||||||
If you want to load a configuration file through the Docker image you will have to declare the
|
If you want to load a configuration file through the Docker image you will have to declare the
|
||||||
[`CONFIG` environment variable](../usage/cli.md#environment-variables) pointing to the assigned configuration file:
|
[`CONFIG` environment variable with `serve` command](../usage/command-line.md#serve) pointing to the assigned
|
||||||
|
configuration file:
|
||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
This is no longer required since version 4.2.0. Now configuration file can be loaded from
|
This is no longer required since version 4.2.0. Now configuration file can be loaded from
|
||||||
|
|||||||
15
docs/migration/v4.0-to-v4.17.md
Normal file
15
docs/migration/v4.0-to-v4.17.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Diun v4.0 to v4.17
|
||||||
|
|
||||||
|
## New CLI
|
||||||
|
|
||||||
|
CLI has changed since 4.17 and includes [`serve` command](../usage/command-line.md#serve) to start Diun:
|
||||||
|
|
||||||
|
!!! example "v4.0"
|
||||||
|
```shell
|
||||||
|
diun --config diun.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! example "v4.17"
|
||||||
|
```shell
|
||||||
|
diun serve --config diun.yml
|
||||||
|
```
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# Command Line
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```shell
|
|
||||||
diun [options]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
```
|
|
||||||
$ diun --help
|
|
||||||
Usage: diun
|
|
||||||
|
|
||||||
Docker image update notifier. More info: https://github.com/crazy-max/diun
|
|
||||||
|
|
||||||
Flags:
|
|
||||||
-h, --help Show context-sensitive help.
|
|
||||||
--version
|
|
||||||
--config=STRING Diun configuration file ($CONFIG).
|
|
||||||
--profiler-path=STRING Base path where profiling files are written
|
|
||||||
($PROFILER_PATH).
|
|
||||||
--profiler=STRING Profiler to use ($PROFILER).
|
|
||||||
--log-level="info" Set log level ($LOG_LEVEL).
|
|
||||||
--log-json Enable JSON logging output ($LOG_JSON).
|
|
||||||
--log-caller Add file:line of the caller to log output
|
|
||||||
($LOG_CALLER).
|
|
||||||
--log-nocolor Disables the colorized output ($LOG_NOCOLOR).
|
|
||||||
--test-notif Test notification settings.
|
|
||||||
```
|
|
||||||
|
|
||||||
## Environment variables
|
|
||||||
|
|
||||||
Following environment variables can be used in place:
|
|
||||||
|
|
||||||
| Name | Default | Description |
|
|
||||||
|--------------------|---------------|---------------|
|
|
||||||
| `CONFIG` | | Diun configuration file |
|
|
||||||
| `PROFILER_PATH` | | Base path where profiling files are written |
|
|
||||||
| `PROFILER` | | [Profiler](../faq.md#profiling) to use |
|
|
||||||
| `LOG_LEVEL` | `info` | Log level output |
|
|
||||||
| `LOG_JSON` | `false` | Enable JSON logging output |
|
|
||||||
| `LOG_CALLER` | `false` | Enable to add `file:line` of the caller |
|
|
||||||
| `LOG_NOCOLOR` | `false` | Disables the colorized output |
|
|
||||||
124
docs/usage/command-line.md
Normal file
124
docs/usage/command-line.md
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
# Command Line
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```shell
|
||||||
|
diun [global options] command [command or global options] [arguments...]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Global options
|
||||||
|
|
||||||
|
All global options can be placed at the command level.
|
||||||
|
|
||||||
|
* `--help`, `-h`: Show context-sensitive help.
|
||||||
|
* `--version`: Show version and exit.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### `serve`
|
||||||
|
|
||||||
|
Starts Diun server.
|
||||||
|
|
||||||
|
* `--config <path>`: Diun configuration file
|
||||||
|
* `--profiler-path <path>`: Base path where profiling files are written
|
||||||
|
* `--profiler <string>`: Profiler to use
|
||||||
|
* `--log-level <string>`: Set log level (default `info`)
|
||||||
|
* `--log-json`: Enable JSON logging output
|
||||||
|
* `--log-caller`: Add `file:line` of the caller to log output
|
||||||
|
* `--log-nocolor`: Disables the colorized output
|
||||||
|
* `--grpc-authority <string>`: Address used to expose the gRPC server (default `:42286`)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
diun serve --config diun.yml --log-level debug
|
||||||
|
```
|
||||||
|
|
||||||
|
Following environment variables can also be used in place:
|
||||||
|
|
||||||
|
| Name | Default | Description |
|
||||||
|
|--------------------|---------------|---------------|
|
||||||
|
| `CONFIG` | | Diun configuration file |
|
||||||
|
| `PROFILER_PATH` | | Base path where profiling files are written |
|
||||||
|
| `PROFILER` | | [Profiler](../faq.md#profiling) to use |
|
||||||
|
| `LOG_LEVEL` | `info` | Log level output |
|
||||||
|
| `LOG_JSON` | `false` | Enable JSON logging output |
|
||||||
|
| `LOG_CALLER` | `false` | Enable to add `file:line` of the caller |
|
||||||
|
| `LOG_NOCOLOR` | `false` | Disables the colorized output |
|
||||||
|
| `GRPC_AUTHORITY` | `:42286` | Address used to expose the gRPC server |
|
||||||
|
|
||||||
|
### `image list`
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
Diun needs to be started through [`serve`](#serve) command to be able to use this command.
|
||||||
|
|
||||||
|
List images in database.
|
||||||
|
|
||||||
|
* `--raw`: JSON output
|
||||||
|
* `--grpc-authority <string>`: Link to Diun gRPC API (default `127.0.0.1:42286`)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
diun image list
|
||||||
|
```
|
||||||
|
```shell
|
||||||
|
diun image list --raw
|
||||||
|
```
|
||||||
|
|
||||||
|
### `image inspect`
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
Diun needs to be started through [`serve`](#serve) command to be able to use this command.
|
||||||
|
|
||||||
|
Display information of an image in database.
|
||||||
|
|
||||||
|
* `--image`: Image to inspect (**required**)
|
||||||
|
* `--raw`: JSON output
|
||||||
|
* `--grpc-authority <string>`: Link to Diun gRPC API (default `127.0.0.1:42286`)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
diun image inspect alpine
|
||||||
|
```
|
||||||
|
```shell
|
||||||
|
diun image inspect drone/drone --raw
|
||||||
|
```
|
||||||
|
|
||||||
|
### `image remove`
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
Diun needs to be started through [`serve`](#serve) command to be able to use this command.
|
||||||
|
|
||||||
|
Remove an image manifest from database.
|
||||||
|
|
||||||
|
* `--image`: Image to remove (**required**)
|
||||||
|
* `--grpc-authority <string>`: Link to Diun gRPC API (default `127.0.0.1:42286`)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
diun image remove alpine:latest
|
||||||
|
```
|
||||||
|
```shell
|
||||||
|
diun image inspect drone/drone
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
All manifest for an image will be removed if no tag is specified
|
||||||
|
|
||||||
|
### `notif test`
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
Diun needs to be started through [`serve`](#serve) command to be able to use this command.
|
||||||
|
|
||||||
|
Test notification settings.
|
||||||
|
|
||||||
|
* `--grpc-authority <string>`: Link to Diun gRPC API (default `127.0.0.1:42286`)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
diun notif test
|
||||||
|
```
|
||||||
8
go.mod
8
go.mod
@@ -1,6 +1,6 @@
|
|||||||
module github.com/crazy-max/diun/v4
|
module github.com/crazy-max/diun/v4
|
||||||
|
|
||||||
go 1.15
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alecthomas/kong v0.2.16
|
github.com/alecthomas/kong v0.2.16
|
||||||
@@ -11,6 +11,7 @@ require (
|
|||||||
github.com/crazy-max/gonfig v0.4.0
|
github.com/crazy-max/gonfig v0.4.0
|
||||||
github.com/docker/docker v20.10.6+incompatible
|
github.com/docker/docker v20.10.6+incompatible
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
|
github.com/docker/go-units v0.4.0
|
||||||
github.com/eclipse/paho.mqtt.golang v1.3.3
|
github.com/eclipse/paho.mqtt.golang v1.3.3
|
||||||
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
|
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
|
||||||
github.com/go-playground/validator/v10 v10.5.0
|
github.com/go-playground/validator/v10 v10.5.0
|
||||||
@@ -18,6 +19,7 @@ require (
|
|||||||
github.com/gregdel/pushover v0.0.0-20201104094836-ddbe0c1d3a38
|
github.com/gregdel/pushover v0.0.0-20201104094836-ddbe0c1d3a38
|
||||||
github.com/hako/durafmt v0.0.0-20190612201238-650ed9f29a84
|
github.com/hako/durafmt v0.0.0-20190612201238-650ed9f29a84
|
||||||
github.com/imdario/mergo v0.3.12
|
github.com/imdario/mergo v0.3.12
|
||||||
|
github.com/jedib0t/go-pretty/v6 v6.2.1
|
||||||
github.com/matcornic/hermes/v2 v2.1.0
|
github.com/matcornic/hermes/v2 v2.1.0
|
||||||
github.com/matrix-org/gomatrix v0.0.0-20200501121722-e5578b12c752
|
github.com/matrix-org/gomatrix v0.0.0-20200501121722-e5578b12c752
|
||||||
github.com/microcosm-cc/bluemonday v1.0.9
|
github.com/microcosm-cc/bluemonday v1.0.9
|
||||||
@@ -35,7 +37,11 @@ require (
|
|||||||
github.com/streadway/amqp v0.0.0-20200108173154-1c71cc93ed71
|
github.com/streadway/amqp v0.0.0-20200108173154-1c71cc93ed71
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
||||||
|
github.com/tidwall/pretty v1.1.0
|
||||||
go.etcd.io/bbolt v1.3.5
|
go.etcd.io/bbolt v1.3.5
|
||||||
|
google.golang.org/grpc v1.37.0
|
||||||
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0
|
||||||
|
google.golang.org/protobuf v1.26.0
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
|
|||||||
30
go.sum
30
go.sum
@@ -298,8 +298,9 @@ github.com/cilium/ebpf v0.2.0 h1:Fv93L3KKckEcEHR3oApXVzyBTDA8WAm6VXhPE00N3f8=
|
|||||||
github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
|
github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
|
||||||
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU=
|
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y=
|
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y=
|
||||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||||
github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58 h1:hHWif/4GirK3P5uvCyyj941XSVIQDzuJhbEguCICdPE=
|
github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58 h1:hHWif/4GirK3P5uvCyyj941XSVIQDzuJhbEguCICdPE=
|
||||||
@@ -458,8 +459,9 @@ github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao
|
|||||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4 h1:rEvIZUSZ3fx39WIi3JkQqQBitGwpELBIYWeBVh6wn+E=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d h1:QyzYnTnPE15SQyUeqU6qLbWxMkwyAyu+vGksa0b7j00=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
@@ -478,6 +480,8 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu
|
|||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
|
github.com/fzipp/gocyclo v0.3.1 h1:A9UeX3HJSXTBzvHzhqoYVuE0eAhe+aM8XBCCwsPMZOc=
|
||||||
|
github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E=
|
||||||
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 h1:LofdAjjjqCSXMwLGgOgnE+rdPuvX9DxCqaHwKy7i/ko=
|
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 h1:LofdAjjjqCSXMwLGgOgnE+rdPuvX9DxCqaHwKy7i/ko=
|
||||||
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
@@ -616,8 +620,9 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
|
|||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
|
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
|
||||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
@@ -667,8 +672,9 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-containerregistry v0.0.0-20191010200024-a3d713f9b7f8/go.mod h1:KyKXa9ciM8+lgMXwOVsXi7UxGrsf9mM61Mzs+xKUrKE=
|
github.com/google/go-containerregistry v0.0.0-20191010200024-a3d713f9b7f8/go.mod h1:KyKXa9ciM8+lgMXwOVsXi7UxGrsf9mM61Mzs+xKUrKE=
|
||||||
github.com/google/go-containerregistry v0.1.2 h1:YjFNKqxzWUVZND8d4ItF9wuYlE75WQfECE7yKX/Nu3o=
|
github.com/google/go-containerregistry v0.1.2 h1:YjFNKqxzWUVZND8d4ItF9wuYlE75WQfECE7yKX/Nu3o=
|
||||||
github.com/google/go-containerregistry v0.1.2/go.mod h1:GPivBPgdAyd2SU+vf6EpsgOtWDuPqjW0hJZt4rNdTZ4=
|
github.com/google/go-containerregistry v0.1.2/go.mod h1:GPivBPgdAyd2SU+vf6EpsgOtWDuPqjW0hJZt4rNdTZ4=
|
||||||
@@ -856,6 +862,8 @@ github.com/jarcoal/httpmock v1.0.5 h1:cHtVEcTxRSX4J0je7mWPfc9BpDpqzXSJ5HbymZmyHc
|
|||||||
github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||||
github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0 h1:xqgexXAGQgY3HAjNPSaCqn5Aahbo5TKsmhp8VRfr1iQ=
|
github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0 h1:xqgexXAGQgY3HAjNPSaCqn5Aahbo5TKsmhp8VRfr1iQ=
|
||||||
github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
||||||
|
github.com/jedib0t/go-pretty/v6 v6.2.1 h1:O/3XdNfyWSyVLLIt1EeDhfP8AhNMjtBSh0MuZ4frg6U=
|
||||||
|
github.com/jedib0t/go-pretty/v6 v6.2.1/go.mod h1:+nE9fyyHGil+PuISTCrp7avEdo6bqoMwqZnuiK2r2a0=
|
||||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1 h1:ujPKutqRlJtcfWk6toYVYagwra7HQHbXOaS171b4Tg8=
|
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1 h1:ujPKutqRlJtcfWk6toYVYagwra7HQHbXOaS171b4Tg8=
|
||||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||||
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a h1:GmsqmapfzSJkm28dhRoHz2tLRbJmqhU86IPgBtN3mmk=
|
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a h1:GmsqmapfzSJkm28dhRoHz2tLRbJmqhU86IPgBtN3mmk=
|
||||||
@@ -977,6 +985,7 @@ github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHX
|
|||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
|
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
|
||||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||||
@@ -1152,6 +1161,7 @@ github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV
|
|||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||||
github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
|
github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
|
||||||
github.com/pkg/profile v1.6.0 h1:hUDfIISABYI59DyeB3OTay/HxSRwTQ8rB/H83k6r5dM=
|
github.com/pkg/profile v1.6.0 h1:hUDfIISABYI59DyeB3OTay/HxSRwTQ8rB/H83k6r5dM=
|
||||||
github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
|
github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
|
||||||
@@ -1348,6 +1358,8 @@ github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6
|
|||||||
github.com/tetafro/godot v0.3.7/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0=
|
github.com/tetafro/godot v0.3.7/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0=
|
||||||
github.com/tetafro/godot v0.4.2 h1:Dib7un+rYJFUi8vN0Bk6EHheKy6fv6ZzFURHw75g6m8=
|
github.com/tetafro/godot v0.4.2 h1:Dib7un+rYJFUi8vN0Bk6EHheKy6fv6ZzFURHw75g6m8=
|
||||||
github.com/tetafro/godot v0.4.2/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0=
|
github.com/tetafro/godot v0.4.2/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0=
|
||||||
|
github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8=
|
||||||
|
github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
|
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
|
||||||
github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94 h1:ig99OeTyDwQWhPe2iw9lwfQVF1KB3Q4fpP3X7/2VBG8=
|
github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94 h1:ig99OeTyDwQWhPe2iw9lwfQVF1KB3Q4fpP3X7/2VBG8=
|
||||||
github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
|
github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
|
||||||
@@ -1611,6 +1623,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@@ -1868,8 +1881,11 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
|
|||||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||||
google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o=
|
|
||||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
|
google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c=
|
||||||
|
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||||
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE=
|
||||||
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
@@ -1879,8 +1895,10 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
|||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=
|
gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=
|
||||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||||
|
|||||||
40
hack/gen.Dockerfile
Normal file
40
hack/gen.Dockerfile
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# syntax=docker/dockerfile:1.2
|
||||||
|
ARG GO_VERSION
|
||||||
|
ARG PROTOC_VERSION
|
||||||
|
ARG GLIBC_VERSION=2.33-r0
|
||||||
|
|
||||||
|
FROM golang:${GO_VERSION}-alpine AS base
|
||||||
|
ARG GLIBC_VERSION
|
||||||
|
RUN apk add --no-cache curl file git unzip \
|
||||||
|
&& curl -sSL "https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub" -o "/etc/apk/keys/sgerrand.rsa.pub" \
|
||||||
|
&& curl -sSL "https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VERSION}/glibc-${GLIBC_VERSION}.apk" -o "glibc.apk" \
|
||||||
|
&& apk add glibc.apk \
|
||||||
|
&& rm /etc/apk/keys/sgerrand.rsa.pub glibc.apk
|
||||||
|
ARG PROTOC_VERSION
|
||||||
|
RUN curl -sSL "https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-x86_64.zip" -o "protoc.zip" \
|
||||||
|
&& unzip "protoc.zip" -d "/usr/local" \
|
||||||
|
&& protoc --version \
|
||||||
|
&& rm "protoc.zip"
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
FROM base AS gomod
|
||||||
|
RUN --mount=type=bind,target=.,rw \
|
||||||
|
--mount=type=cache,target=/go/pkg/mod \
|
||||||
|
go mod tidy && go mod download && go install -v $(sed -n -e 's|^\s*_\s*"\(.*\)".*$|\1| p' tools.go)
|
||||||
|
|
||||||
|
FROM gomod AS generate
|
||||||
|
RUN --mount=type=bind,target=.,rw \
|
||||||
|
--mount=type=cache,target=/go/pkg/mod \
|
||||||
|
go generate ./... && mkdir /out && cp -Rf pb /out
|
||||||
|
|
||||||
|
FROM scratch AS update
|
||||||
|
COPY --from=generate /out /
|
||||||
|
|
||||||
|
FROM generate AS validate
|
||||||
|
RUN --mount=type=bind,target=.,rw \
|
||||||
|
git add -A && cp -rf /out/* .; \
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then \
|
||||||
|
echo >&2 'ERROR: Generate result differs. Please update with "docker buildx bake gen-update"'; \
|
||||||
|
git status --porcelain; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/crazy-max/diun/v4/internal/config"
|
"github.com/crazy-max/diun/v4/internal/config"
|
||||||
"github.com/crazy-max/diun/v4/internal/db"
|
"github.com/crazy-max/diun/v4/internal/db"
|
||||||
|
"github.com/crazy-max/diun/v4/internal/grpc"
|
||||||
"github.com/crazy-max/diun/v4/internal/logging"
|
"github.com/crazy-max/diun/v4/internal/logging"
|
||||||
"github.com/crazy-max/diun/v4/internal/model"
|
"github.com/crazy-max/diun/v4/internal/model"
|
||||||
"github.com/crazy-max/diun/v4/internal/notif"
|
"github.com/crazy-max/diun/v4/internal/notif"
|
||||||
@@ -16,7 +17,6 @@ import (
|
|||||||
filePrd "github.com/crazy-max/diun/v4/internal/provider/file"
|
filePrd "github.com/crazy-max/diun/v4/internal/provider/file"
|
||||||
kubernetesPrd "github.com/crazy-max/diun/v4/internal/provider/kubernetes"
|
kubernetesPrd "github.com/crazy-max/diun/v4/internal/provider/kubernetes"
|
||||||
swarmPrd "github.com/crazy-max/diun/v4/internal/provider/swarm"
|
swarmPrd "github.com/crazy-max/diun/v4/internal/provider/swarm"
|
||||||
"github.com/crazy-max/diun/v4/pkg/registry"
|
|
||||||
"github.com/crazy-max/gohealthchecks"
|
"github.com/crazy-max/gohealthchecks"
|
||||||
"github.com/hako/durafmt"
|
"github.com/hako/durafmt"
|
||||||
"github.com/panjf2000/ants/v2"
|
"github.com/panjf2000/ants/v2"
|
||||||
@@ -29,10 +29,13 @@ import (
|
|||||||
type Diun struct {
|
type Diun struct {
|
||||||
meta model.Meta
|
meta model.Meta
|
||||||
cfg *config.Config
|
cfg *config.Config
|
||||||
cron *cron.Cron
|
|
||||||
db *db.Client
|
db *db.Client
|
||||||
|
grpc *grpc.Client
|
||||||
hc *gohealthchecks.Client
|
hc *gohealthchecks.Client
|
||||||
notif *notif.Client
|
notif *notif.Client
|
||||||
|
|
||||||
|
cron *cron.Cron
|
||||||
jobID cron.EntryID
|
jobID cron.EntryID
|
||||||
locker uint32
|
locker uint32
|
||||||
pool *ants.PoolWithFunc
|
pool *ants.PoolWithFunc
|
||||||
@@ -40,7 +43,7 @@ type Diun struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New creates new diun instance
|
// New creates new diun instance
|
||||||
func New(meta model.Meta, cli model.Cli, cfg *config.Config) (*Diun, error) {
|
func New(meta model.Meta, cfg *config.Config, grpcAuthority string) (*Diun, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
diun := &Diun{
|
diun := &Diun{
|
||||||
@@ -56,11 +59,14 @@ func New(meta model.Meta, cli model.Cli, cfg *config.Config) (*Diun, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cli.TestNotif {
|
|
||||||
diun.db, err = db.New(*cfg.Db)
|
diun.db, err = db.New(*cfg.Db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diun.grpc, err = grpc.New(grpcAuthority, diun.db, diun.notif)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Watch.Healthchecks != nil {
|
if cfg.Watch.Healthchecks != nil {
|
||||||
@@ -89,6 +95,13 @@ func (di *Diun) Start() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start GRPC server
|
||||||
|
go func() {
|
||||||
|
if err := di.grpc.Start(); err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Failed to start GRPC server")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Run on startup
|
// Run on startup
|
||||||
di.Run()
|
di.Run()
|
||||||
|
|
||||||
@@ -157,7 +170,7 @@ func (di *Diun) Run() {
|
|||||||
di.createJob(job)
|
di.createJob(job)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dokcerfile provider
|
// Dockerfile provider
|
||||||
for _, job := range dockerfilePrd.New(di.cfg.Providers.Dockerfile).ListJob() {
|
for _, job := range dockerfilePrd.New(di.cfg.Providers.Dockerfile).ListJob() {
|
||||||
di.createJob(job)
|
di.createJob(job)
|
||||||
}
|
}
|
||||||
@@ -178,51 +191,8 @@ func (di *Diun) Close() {
|
|||||||
if di.cron != nil {
|
if di.cron != nil {
|
||||||
di.cron.Stop()
|
di.cron.Stop()
|
||||||
}
|
}
|
||||||
|
di.grpc.Stop()
|
||||||
if err := di.db.Close(); err != nil {
|
if err := di.db.Close(); err != nil {
|
||||||
log.Warn().Err(err).Msg("Cannot close database")
|
log.Warn().Err(err).Msg("Cannot close database")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestNotif test the notification settings
|
|
||||||
func (di *Diun) TestNotif() {
|
|
||||||
createdAt, _ := time.Parse("2006-01-02T15:04:05Z", "2020-03-26T12:23:56Z")
|
|
||||||
image, _ := registry.ParseImage(registry.ParseImageOptions{
|
|
||||||
Name: "diun/testnotif:latest",
|
|
||||||
})
|
|
||||||
image.HubLink = ""
|
|
||||||
|
|
||||||
log.Info().Msg("Testing notification settings...")
|
|
||||||
di.notif.Send(model.NotifEntry{
|
|
||||||
Status: "new",
|
|
||||||
Provider: "file",
|
|
||||||
Image: image,
|
|
||||||
Manifest: registry.Manifest{
|
|
||||||
Name: "diun/testnotif",
|
|
||||||
Tag: "latest",
|
|
||||||
MIMEType: "application/vnd.docker.distribution.manifest.list.v2+json",
|
|
||||||
Digest: "sha256:216e3ae7de4ca8b553eb11ef7abda00651e79e537e85c46108284e5e91673e01",
|
|
||||||
Created: &createdAt,
|
|
||||||
DockerVersion: "",
|
|
||||||
Labels: map[string]string{
|
|
||||||
"maintainer": "CrazyMax",
|
|
||||||
"org.label-schema.build-date": "2020-03-26T12:23:56Z",
|
|
||||||
"org.label-schema.description": "Docker image update notifier",
|
|
||||||
"org.label-schema.name": "Diun",
|
|
||||||
"org.label-schema.schema-version": "1.0",
|
|
||||||
"org.label-schema.url": "https://github.com/crazy-max/diun",
|
|
||||||
"org.label-schema.vcs-ref": "e13f097c",
|
|
||||||
"org.label-schema.vcs-url": "https://github.com/crazy-max/diun",
|
|
||||||
"org.label-schema.vendor": "CrazyMax",
|
|
||||||
"org.label-schema.version": "x.x.x",
|
|
||||||
},
|
|
||||||
Layers: []string{
|
|
||||||
"sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819",
|
|
||||||
"sha256:166c6f165b73185ede72415d780538a55c0c8e854bd177925bc007193e5b0d1b",
|
|
||||||
"sha256:e05682efa9cc9d6239b2b9252fe0dc1e58d6e1585679733bb94a6549d49e9b10",
|
|
||||||
"sha256:c6a5bfed445b3ed7e85523cd73c6532ac9f9b72bb588ca728fd5b33987ca6538",
|
|
||||||
"sha256:df2140efb8abeb727ef0b27ff158b7010a7941eb1cfdade505f510a6e1eaf016",
|
|
||||||
},
|
|
||||||
Platform: "linux/amd64",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -22,14 +22,14 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load returns Config struct
|
// Load returns Config struct
|
||||||
func Load(cli model.Cli) (*Config, error) {
|
func Load(config string) (*Config, error) {
|
||||||
cfg := Config{
|
cfg := Config{
|
||||||
Db: (&model.Db{}).GetDefaults(),
|
Db: (&model.Db{}).GetDefaults(),
|
||||||
Watch: (&model.Watch{}).GetDefaults(),
|
Watch: (&model.Watch{}).GetDefaults(),
|
||||||
}
|
}
|
||||||
|
|
||||||
fileLoader := gonfig.NewFileLoader(gonfig.FileLoaderConfig{
|
fileLoader := gonfig.NewFileLoader(gonfig.FileLoaderConfig{
|
||||||
Filename: cli.Cfgfile,
|
Filename: config,
|
||||||
Finder: gonfig.Finder{
|
Finder: gonfig.Finder{
|
||||||
BasePaths: []string{"/etc/diun/diun", "$XDG_CONFIG_HOME/diun", "$HOME/.config/diun", "./diun"},
|
BasePaths: []string{"/etc/diun/diun", "$XDG_CONFIG_HOME/diun", "$HOME/.config/diun", "./diun"},
|
||||||
Extensions: []string{"yaml", "yml"},
|
Extensions: []string{"yaml", "yml"},
|
||||||
@@ -54,14 +54,14 @@ func Load(cli model.Cli) (*Config, error) {
|
|||||||
log.Info().Msgf("Configuration loaded from %d environment variable(s)", len(envLoader.GetVars()))
|
log.Info().Msgf("Configuration loaded from %d environment variable(s)", len(envLoader.GetVars()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cfg.validate(cli); err != nil {
|
if err := cfg.validate(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &cfg, nil
|
return &cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *Config) validate(cli model.Cli) error {
|
func (cfg *Config) validate() error {
|
||||||
if len(cfg.Db.Path) > 0 {
|
if len(cfg.Db.Path) > 0 {
|
||||||
if err := os.MkdirAll(path.Dir(cfg.Db.Path), os.ModePerm); err != nil {
|
if err := os.MkdirAll(path.Dir(cfg.Db.Path), os.ModePerm); err != nil {
|
||||||
return errors.Wrap(err, "Cannot create database destination folder")
|
return errors.Wrap(err, "Cannot create database destination folder")
|
||||||
@@ -72,11 +72,7 @@ func (cfg *Config) validate(cli model.Cli) error {
|
|||||||
return errors.New("Healthchecks UUID is required")
|
return errors.New("Healthchecks UUID is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Notif == nil && cli.TestNotif {
|
if cfg.Providers == nil {
|
||||||
return errors.New("At least one notifier is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Providers == nil && !cli.TestNotif {
|
|
||||||
return errors.New("At least one provider is required")
|
return errors.New("At least one provider is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,55 +18,33 @@ import (
|
|||||||
func TestLoadFile(t *testing.T) {
|
func TestLoadFile(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
cli model.Cli
|
cfg string
|
||||||
wantData *config.Config
|
wantData *config.Config
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Failed on non-existing file",
|
name: "Failed on non-existing file",
|
||||||
cli: model.Cli{
|
cfg: "",
|
||||||
TestNotif: false,
|
|
||||||
},
|
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Fail on wrong file format",
|
name: "Fail on wrong file format",
|
||||||
cli: model.Cli{
|
cfg: "./fixtures/config.invalid.yml",
|
||||||
Cfgfile: "./fixtures/config.invalid.yml",
|
|
||||||
TestNotif: false,
|
|
||||||
},
|
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Fail on no UUID for Healthchecks",
|
name: "Fail on no UUID for Healthchecks",
|
||||||
cli: model.Cli{
|
cfg: "./fixtures/config.err.hc.yml",
|
||||||
Cfgfile: "./fixtures/config.err.hc.yml",
|
|
||||||
TestNotif: false,
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Fail on no notifier if test notif",
|
|
||||||
cli: model.Cli{
|
|
||||||
Cfgfile: "./fixtures/config.err.notif.yml",
|
|
||||||
TestNotif: true,
|
|
||||||
},
|
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Fail on no provider",
|
name: "Fail on no provider",
|
||||||
cli: model.Cli{
|
cfg: "./fixtures/config.err.provider.yml",
|
||||||
Cfgfile: "./fixtures/config.err.provider.yml",
|
|
||||||
TestNotif: false,
|
|
||||||
},
|
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Success",
|
name: "Success",
|
||||||
cli: model.Cli{
|
cfg: "./fixtures/config.test.yml",
|
||||||
Cfgfile: "./fixtures/config.test.yml",
|
|
||||||
TestNotif: false,
|
|
||||||
},
|
|
||||||
wantData: &config.Config{
|
wantData: &config.Config{
|
||||||
Db: &model.Db{
|
Db: &model.Db{
|
||||||
Path: "diun.db",
|
Path: "diun.db",
|
||||||
@@ -218,7 +196,7 @@ func TestLoadFile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range cases {
|
for _, tt := range cases {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
cfg, err := config.Load(tt.cli)
|
cfg, err := config.Load(tt.cfg)
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
@@ -237,7 +215,7 @@ func TestLoadEnv(t *testing.T) {
|
|||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
cli model.Cli
|
cfg string
|
||||||
environ []string
|
environ []string
|
||||||
expected interface{}
|
expected interface{}
|
||||||
wantErr bool
|
wantErr bool
|
||||||
@@ -365,7 +343,7 @@ func TestLoadEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := config.Load(tt.cli)
|
cfg, err := config.Load(tt.cfg)
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
@@ -382,17 +360,14 @@ func TestLoadMixed(t *testing.T) {
|
|||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
cli model.Cli
|
cfg string
|
||||||
environ []string
|
environ []string
|
||||||
expected interface{}
|
expected interface{}
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "env vars and invalid file",
|
desc: "env vars and invalid file",
|
||||||
cli: model.Cli{
|
cfg: "./fixtures/config.invalid.yml",
|
||||||
Cfgfile: "./fixtures/config.invalid.yml",
|
|
||||||
TestNotif: false,
|
|
||||||
},
|
|
||||||
environ: []string{
|
environ: []string{
|
||||||
"DIUN_PROVIDERS_DOCKER=true",
|
"DIUN_PROVIDERS_DOCKER=true",
|
||||||
},
|
},
|
||||||
@@ -401,10 +376,7 @@ func TestLoadMixed(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "docker provider (file) and notif mails (envs)",
|
desc: "docker provider (file) and notif mails (envs)",
|
||||||
cli: model.Cli{
|
cfg: "./fixtures/config.docker.yml",
|
||||||
Cfgfile: "./fixtures/config.docker.yml",
|
|
||||||
TestNotif: false,
|
|
||||||
},
|
|
||||||
environ: []string{
|
environ: []string{
|
||||||
"DIUN_NOTIF_MAIL_HOST=127.0.0.1",
|
"DIUN_NOTIF_MAIL_HOST=127.0.0.1",
|
||||||
"DIUN_NOTIF_MAIL_PORT=25",
|
"DIUN_NOTIF_MAIL_PORT=25",
|
||||||
@@ -441,10 +413,7 @@ func TestLoadMixed(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "file provider and notif webhook env override",
|
desc: "file provider and notif webhook env override",
|
||||||
cli: model.Cli{
|
cfg: "./fixtures/config.file.yml",
|
||||||
Cfgfile: "./fixtures/config.file.yml",
|
|
||||||
TestNotif: false,
|
|
||||||
},
|
|
||||||
environ: []string{
|
environ: []string{
|
||||||
"DIUN_NOTIF_WEBHOOK_ENDPOINT=http://webhook.foo.com/sd54qad89azd5a",
|
"DIUN_NOTIF_WEBHOOK_ENDPOINT=http://webhook.foo.com/sd54qad89azd5a",
|
||||||
"DIUN_NOTIF_WEBHOOK_HEADERS_AUTHORIZATION=Token78910",
|
"DIUN_NOTIF_WEBHOOK_HEADERS_AUTHORIZATION=Token78910",
|
||||||
@@ -488,7 +457,7 @@ func TestLoadMixed(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := config.Load(tt.cli)
|
cfg, err := config.Load(tt.cfg)
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
@@ -503,19 +472,16 @@ func TestLoadMixed(t *testing.T) {
|
|||||||
func TestValidation(t *testing.T) {
|
func TestValidation(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
cli model.Cli
|
cfg string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Success",
|
name: "Success",
|
||||||
cli: model.Cli{
|
cfg: "./fixtures/config.validate.yml",
|
||||||
Cfgfile: "./fixtures/config.validate.yml",
|
|
||||||
TestNotif: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range cases {
|
for _, tt := range cases {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
cfg, err := config.Load(tt.cli)
|
cfg, err := config.Load(tt.cfg)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
dec, err := env.Encode("DIUN_", cfg)
|
dec, err := env.Encode("DIUN_", cfg)
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
providers:
|
|
||||||
docker: {}
|
|
||||||
@@ -3,6 +3,7 @@ package db
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/crazy-max/diun/v4/pkg/registry"
|
"github.com/crazy-max/diun/v4/pkg/registry"
|
||||||
bolt "go.etcd.io/bbolt"
|
bolt "go.etcd.io/bbolt"
|
||||||
@@ -25,6 +26,25 @@ func (c *Client) First(image registry.Image) (bool, error) {
|
|||||||
return !found, err
|
return !found, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListManifest return a list of Docker images manifests
|
||||||
|
func (c *Client) ListManifest() ([]registry.Manifest, error) {
|
||||||
|
var manifests []registry.Manifest
|
||||||
|
|
||||||
|
err := c.View(func(tx *bolt.Tx) error {
|
||||||
|
c := tx.Bucket([]byte(bucketManifest)).Cursor()
|
||||||
|
for k, v := c.First(); k != nil; k, v = c.Next() {
|
||||||
|
var manifest registry.Manifest
|
||||||
|
if err := json.Unmarshal(v, &manifest); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
manifests = append(manifests, manifest)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return manifests, err
|
||||||
|
}
|
||||||
|
|
||||||
// GetManifest returns Docker image manifest
|
// GetManifest returns Docker image manifest
|
||||||
func (c *Client) GetManifest(image registry.Image) (registry.Manifest, error) {
|
func (c *Client) GetManifest(image registry.Image) (registry.Manifest, error) {
|
||||||
var manifest registry.Manifest
|
var manifest registry.Manifest
|
||||||
@@ -43,11 +63,37 @@ func (c *Client) GetManifest(image registry.Image) (registry.Manifest, error) {
|
|||||||
// PutManifest add Docker image manifest in db
|
// PutManifest add Docker image manifest in db
|
||||||
func (c *Client) PutManifest(image registry.Image, manifest registry.Manifest) error {
|
func (c *Client) PutManifest(image registry.Image, manifest registry.Manifest) error {
|
||||||
entryBytes, _ := json.Marshal(manifest)
|
entryBytes, _ := json.Marshal(manifest)
|
||||||
|
return c.Update(func(tx *bolt.Tx) error {
|
||||||
err := c.Update(func(tx *bolt.Tx) error {
|
|
||||||
b := tx.Bucket([]byte(bucketManifest))
|
b := tx.Bucket([]byte(bucketManifest))
|
||||||
return b.Put([]byte(image.String()), entryBytes)
|
return b.Put([]byte(image.String()), entryBytes)
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteManifest deletes a Docker image manifest
|
||||||
|
func (c *Client) DeleteManifest(manifest registry.Manifest) error {
|
||||||
|
return c.Update(func(tx *bolt.Tx) error {
|
||||||
|
return tx.Bucket([]byte(bucketManifest)).Delete([]byte(fmt.Sprintf("%s:%s", manifest.Name, manifest.Tag)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListImage return a list of Docker images with their linked manifests
|
||||||
|
func (c *Client) ListImage() (map[string][]registry.Manifest, error) {
|
||||||
|
images := make(map[string][]registry.Manifest)
|
||||||
|
|
||||||
|
err := c.View(func(tx *bolt.Tx) error {
|
||||||
|
c := tx.Bucket([]byte(bucketManifest)).Cursor()
|
||||||
|
for k, v := c.First(); k != nil; k, v = c.Next() {
|
||||||
|
var manifest registry.Manifest
|
||||||
|
if err := json.Unmarshal(v, &manifest); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if _, ok := images[manifest.Name]; !ok {
|
||||||
|
images[manifest.Name] = []registry.Manifest{}
|
||||||
|
}
|
||||||
|
images[manifest.Name] = append(images[manifest.Name], manifest)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return images, err
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,11 +29,8 @@ func (c *Client) ReadMetadata() error {
|
|||||||
// WriteMetadata writes db metadata
|
// WriteMetadata writes db metadata
|
||||||
func (c *Client) WriteMetadata(metadata Metadata) error {
|
func (c *Client) WriteMetadata(metadata Metadata) error {
|
||||||
entryBytes, _ := json.Marshal(metadata)
|
entryBytes, _ := json.Marshal(metadata)
|
||||||
|
return c.Update(func(tx *bolt.Tx) error {
|
||||||
err := c.Update(func(tx *bolt.Tx) error {
|
|
||||||
b := tx.Bucket([]byte(bucketMetadata))
|
b := tx.Bucket([]byte(bucketMetadata))
|
||||||
return b.Put([]byte(metadataKey), entryBytes)
|
return b.Put([]byte(metadataKey), entryBytes)
|
||||||
})
|
})
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,9 +75,5 @@ func (c *Client) migration2() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := tx.Commit(); err != nil {
|
return tx.Commit()
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
58
internal/grpc/client.go
Normal file
58
internal/grpc/client.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/crazy-max/diun/v4/internal/db"
|
||||||
|
grpclogger "github.com/crazy-max/diun/v4/internal/grpc/logger"
|
||||||
|
"github.com/crazy-max/diun/v4/internal/notif"
|
||||||
|
"github.com/crazy-max/diun/v4/pb"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client represents an active grpc object
|
||||||
|
type Client struct {
|
||||||
|
server *grpc.Server
|
||||||
|
authority string
|
||||||
|
db *db.Client
|
||||||
|
notif *notif.Client
|
||||||
|
pb.UnimplementedImageServiceServer
|
||||||
|
pb.UnimplementedNotifServiceServer
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new grpc instance
|
||||||
|
func New(authority string, db *db.Client, notif *notif.Client) (*Client, error) {
|
||||||
|
grpclogger.SetGrpcLogger(log.Level(zerolog.ErrorLevel))
|
||||||
|
|
||||||
|
c := &Client{
|
||||||
|
authority: authority,
|
||||||
|
db: db,
|
||||||
|
notif: notif,
|
||||||
|
}
|
||||||
|
|
||||||
|
c.server = grpc.NewServer()
|
||||||
|
pb.RegisterImageServiceServer(c.server, c)
|
||||||
|
pb.RegisterNotifServiceServer(c.server, c)
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start runs the grpc server
|
||||||
|
func (c *Client) Start() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
lis, err := net.Listen("tcp", c.authority)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Cannot create gRPC listener")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.server.Serve(lis)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop stops the grpc server
|
||||||
|
func (c *Client) Stop() {
|
||||||
|
c.server.GracefulStop()
|
||||||
|
}
|
||||||
123
internal/grpc/image.go
Normal file
123
internal/grpc/image.go
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/containers/image/v5/docker/reference"
|
||||||
|
"github.com/crazy-max/diun/v4/pb"
|
||||||
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Client) ImageList(ctx context.Context, request *pb.ImageListRequest) (*pb.ImageListResponse, error) {
|
||||||
|
images, err := c.db.ListImage()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ilr []*pb.ImageListResponse_Image
|
||||||
|
for name, manifests := range images {
|
||||||
|
latest := &manifests[0]
|
||||||
|
for _, manifest := range manifests {
|
||||||
|
if manifest.Created.After(*latest.Created) {
|
||||||
|
latest = &manifest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ilr = append(ilr, &pb.ImageListResponse_Image{
|
||||||
|
Name: name,
|
||||||
|
ManifestsCount: int64(len(manifests)),
|
||||||
|
Latest: &pb.Manifest{
|
||||||
|
Tag: latest.Tag,
|
||||||
|
MimeType: latest.MIMEType,
|
||||||
|
Digest: latest.Digest.String(),
|
||||||
|
Created: timestamppb.New(*latest.Created),
|
||||||
|
Labels: latest.Labels,
|
||||||
|
Platform: latest.Platform,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pb.ImageListResponse{
|
||||||
|
Images: ilr,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) ImageInspect(ctx context.Context, request *pb.ImageInspectRequest) (*pb.ImageInspectResponse, error) {
|
||||||
|
ref, err := reference.ParseNormalizedNamed(request.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
images, err := c.db.ListImage()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := images[ref.Name()]; !ok {
|
||||||
|
return nil, fmt.Errorf("%s not found in database", ref.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
iir := &pb.ImageInspectResponse_Image{
|
||||||
|
Name: ref.Name(),
|
||||||
|
Manifests: []*pb.Manifest{},
|
||||||
|
}
|
||||||
|
for _, manifest := range images[ref.Name()] {
|
||||||
|
iir.Manifests = append(iir.Manifests, &pb.Manifest{
|
||||||
|
Tag: manifest.Tag,
|
||||||
|
MimeType: manifest.MIMEType,
|
||||||
|
Digest: manifest.Digest.String(),
|
||||||
|
Created: timestamppb.New(*manifest.Created),
|
||||||
|
Labels: manifest.Labels,
|
||||||
|
Platform: manifest.Platform,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pb.ImageInspectResponse{
|
||||||
|
Image: iir,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) ImageRemove(ctx context.Context, request *pb.ImageRemoveRequest) (*pb.ImageRemoveResponse, error) {
|
||||||
|
ref, err := reference.ParseNormalizedNamed(request.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
images, err := c.db.ListImage()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := images[ref.Name()]; !ok {
|
||||||
|
return nil, fmt.Errorf("%s not found in database", ref.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
var tag string
|
||||||
|
if tagged, ok := ref.(reference.Tagged); ok {
|
||||||
|
tag = tagged.Tag()
|
||||||
|
}
|
||||||
|
|
||||||
|
var removed []*pb.Manifest
|
||||||
|
for _, manifest := range images[ref.Name()] {
|
||||||
|
if len(tag) == 0 || manifest.Tag == tag {
|
||||||
|
if err = c.db.DeleteManifest(manifest); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b, _ := json.Marshal(manifest)
|
||||||
|
removed = append(removed, &pb.Manifest{
|
||||||
|
Tag: manifest.Tag,
|
||||||
|
MimeType: manifest.MIMEType,
|
||||||
|
Digest: manifest.Digest.String(),
|
||||||
|
Created: timestamppb.New(*manifest.Created),
|
||||||
|
Labels: manifest.Labels,
|
||||||
|
Platform: manifest.Platform,
|
||||||
|
Size: int64(len(b)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pb.ImageRemoveResponse{
|
||||||
|
Manifests: removed,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
94
internal/grpc/logger/logger.go
Normal file
94
internal/grpc/logger/logger.go
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetGrpcLogger(logger zerolog.Logger) {
|
||||||
|
grpclog.SetLoggerV2(wrap(logger))
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrap(l zerolog.Logger) *bridge {
|
||||||
|
return &bridge{l}
|
||||||
|
}
|
||||||
|
|
||||||
|
type bridge struct {
|
||||||
|
zerolog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridge) Info(args ...interface{}) {
|
||||||
|
b.Logger.Info().Msg(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridge) Infoln(args ...interface{}) {
|
||||||
|
b.Logger.Info().Msg(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridge) Infof(format string, args ...interface{}) {
|
||||||
|
b.Logger.Info().Msgf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridge) Warning(args ...interface{}) {
|
||||||
|
b.Logger.Warn().Msg(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridge) Warningln(args ...interface{}) {
|
||||||
|
b.Logger.Warn().Msg(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridge) Warningf(format string, args ...interface{}) {
|
||||||
|
b.Logger.Warn().Msgf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridge) Error(args ...interface{}) {
|
||||||
|
b.Logger.Error().Msg(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridge) Errorln(args ...interface{}) {
|
||||||
|
b.Logger.Error().Msg(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridge) Errorf(format string, args ...interface{}) {
|
||||||
|
b.Logger.Error().Msgf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridge) Fatal(args ...interface{}) {
|
||||||
|
b.Logger.Fatal().Msg(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridge) Fatalln(args ...interface{}) {
|
||||||
|
b.Logger.Fatal().Msg(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridge) Fatalf(format string, args ...interface{}) {
|
||||||
|
b.Logger.Fatal().Msgf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridge) V(verbosity int) bool {
|
||||||
|
// verbosity values:
|
||||||
|
// 0 = info
|
||||||
|
// 1 = warning
|
||||||
|
// 2 = error
|
||||||
|
// 3 = fatal
|
||||||
|
switch b.GetLevel() {
|
||||||
|
case zerolog.PanicLevel:
|
||||||
|
return verbosity > 3
|
||||||
|
case zerolog.FatalLevel:
|
||||||
|
return verbosity == 3
|
||||||
|
case zerolog.ErrorLevel:
|
||||||
|
return verbosity == 2
|
||||||
|
case zerolog.WarnLevel:
|
||||||
|
return verbosity == 1
|
||||||
|
case zerolog.InfoLevel:
|
||||||
|
return verbosity == 0
|
||||||
|
case zerolog.DebugLevel:
|
||||||
|
return true
|
||||||
|
case zerolog.TraceLevel:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
72
internal/grpc/notif.go
Normal file
72
internal/grpc/notif.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/crazy-max/diun/v4/internal/model"
|
||||||
|
"github.com/crazy-max/diun/v4/pb"
|
||||||
|
"github.com/crazy-max/diun/v4/pkg/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Client) NotifTest(ctx context.Context, request *pb.NotifTestRequest) (*pb.NotifTestResponse, error) {
|
||||||
|
createdAt, _ := time.Parse("2006-01-02T15:04:05Z", "2020-03-26T12:23:56Z")
|
||||||
|
image, _ := registry.ParseImage(registry.ParseImageOptions{
|
||||||
|
Name: "diun/testnotif:latest",
|
||||||
|
})
|
||||||
|
image.HubLink = ""
|
||||||
|
|
||||||
|
entry := model.NotifEntry{
|
||||||
|
Status: "new",
|
||||||
|
Provider: "file",
|
||||||
|
Image: image,
|
||||||
|
Manifest: registry.Manifest{
|
||||||
|
Name: "diun/testnotif",
|
||||||
|
Tag: "latest",
|
||||||
|
MIMEType: "application/vnd.docker.distribution.manifest.list.v2+json",
|
||||||
|
Digest: "sha256:216e3ae7de4ca8b553eb11ef7abda00651e79e537e85c46108284e5e91673e01",
|
||||||
|
Created: &createdAt,
|
||||||
|
DockerVersion: "",
|
||||||
|
Labels: map[string]string{
|
||||||
|
"maintainer": "CrazyMax",
|
||||||
|
"org.label-schema.build-date": "2020-03-26T12:23:56Z",
|
||||||
|
"org.label-schema.description": "Docker image update notifier",
|
||||||
|
"org.label-schema.name": "Diun",
|
||||||
|
"org.label-schema.schema-version": "1.0",
|
||||||
|
"org.label-schema.url": "https://github.com/crazy-max/diun",
|
||||||
|
"org.label-schema.vcs-ref": "e13f097c",
|
||||||
|
"org.label-schema.vcs-url": "https://github.com/crazy-max/diun",
|
||||||
|
"org.label-schema.vendor": "CrazyMax",
|
||||||
|
"org.label-schema.version": "x.x.x",
|
||||||
|
},
|
||||||
|
Layers: []string{
|
||||||
|
"sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819",
|
||||||
|
"sha256:166c6f165b73185ede72415d780538a55c0c8e854bd177925bc007193e5b0d1b",
|
||||||
|
"sha256:e05682efa9cc9d6239b2b9252fe0dc1e58d6e1585679733bb94a6549d49e9b10",
|
||||||
|
"sha256:c6a5bfed445b3ed7e85523cd73c6532ac9f9b72bb588ca728fd5b33987ca6538",
|
||||||
|
"sha256:df2140efb8abeb727ef0b27ff158b7010a7941eb1cfdade505f510a6e1eaf016",
|
||||||
|
},
|
||||||
|
Platform: "linux/amd64",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.notif.List()) == 0 {
|
||||||
|
return &pb.NotifTestResponse{
|
||||||
|
Message: "No notifier available",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var sent []string
|
||||||
|
for _, n := range c.notif.List() {
|
||||||
|
if err := n.Send(entry); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sent = append(sent, n.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pb.NotifTestResponse{
|
||||||
|
Message: fmt.Sprintf("Notifcation sent for %s notifier(s)", strings.Join(sent, ", ")),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -6,24 +6,30 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/crazy-max/diun/v4/internal/model"
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
LogLevel string
|
||||||
|
LogJSON bool
|
||||||
|
LogCaller bool
|
||||||
|
LogNoColor bool
|
||||||
|
}
|
||||||
|
|
||||||
// Configure configures logger
|
// Configure configures logger
|
||||||
func Configure(cli *model.Cli) {
|
func Configure(opts Options) {
|
||||||
var err error
|
var err error
|
||||||
var w io.Writer
|
var w io.Writer
|
||||||
|
|
||||||
// Adds support for NO_COLOR. More info https://no-color.org/
|
// Adds support for NO_COLOR. More info https://no-color.org/
|
||||||
_, noColor := os.LookupEnv("NO_COLOR")
|
_, noColor := os.LookupEnv("NO_COLOR")
|
||||||
|
|
||||||
if !cli.LogJSON {
|
if !opts.LogJSON {
|
||||||
w = zerolog.ConsoleWriter{
|
w = zerolog.ConsoleWriter{
|
||||||
Out: os.Stdout,
|
Out: os.Stdout,
|
||||||
NoColor: noColor || cli.LogNoColor,
|
NoColor: noColor || opts.LogNoColor,
|
||||||
TimeFormat: time.RFC1123,
|
TimeFormat: time.RFC1123,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -31,20 +37,20 @@ func Configure(cli *model.Cli) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx := zerolog.New(w).With().Timestamp()
|
ctx := zerolog.New(w).With().Timestamp()
|
||||||
if cli.LogCaller {
|
if opts.LogCaller {
|
||||||
ctx = ctx.Caller()
|
ctx = ctx.Caller()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Logger = ctx.Logger()
|
log.Logger = ctx.Logger()
|
||||||
|
|
||||||
logLevel, err := zerolog.ParseLevel(cli.LogLevel)
|
logLevel, err := zerolog.ParseLevel(opts.LogLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msgf("Unknown log level")
|
log.Fatal().Err(err).Msgf("Unknown log level")
|
||||||
} else {
|
} else {
|
||||||
zerolog.SetGlobalLevel(logLevel)
|
zerolog.SetGlobalLevel(logLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
logrusLevel, err := logrus.ParseLevel(cli.LogLevel)
|
logrusLevel, err := logrus.ParseLevel(opts.LogLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msgf("Unknown log level")
|
log.Fatal().Err(err).Msgf("Unknown log level")
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
package model
|
|
||||||
|
|
||||||
import "github.com/alecthomas/kong"
|
|
||||||
|
|
||||||
// Cli holds command line args, flags and cmds
|
|
||||||
type Cli struct {
|
|
||||||
Version kong.VersionFlag
|
|
||||||
Cfgfile string `kong:"name='config',env='CONFIG',help='Diun configuration file.'"`
|
|
||||||
ProfilerPath string `kong:"name='profiler-path',env='PROFILER_PATH',help='Base path where profiling files are written.'"`
|
|
||||||
Profiler string `kong:"name='profiler',env='PROFILER',help='Profiler to use.'"`
|
|
||||||
LogLevel string `kong:"name='log-level',env='LOG_LEVEL',default='info',help='Set log level.'"`
|
|
||||||
LogJSON bool `kong:"name='log-json',env='LOG_JSON',default='false',help='Enable JSON logging output.'"`
|
|
||||||
LogCaller bool `kong:"name='log-caller',env='LOG_CALLER',default='false',help='Add file:line of the caller to log output.'"`
|
|
||||||
LogNoColor bool `kong:"name='log-nocolor',env='LOG_NOCOLOR',default='false',help='Disables the colorized output.'"`
|
|
||||||
TestNotif bool `kong:"name='test-notif',default='false',help='Test notification settings.'"`
|
|
||||||
}
|
|
||||||
@@ -95,3 +95,8 @@ func (c *Client) Send(entry model.NotifEntry) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// List returns created notifiers
|
||||||
|
func (c *Client) List() []notifier.Notifier {
|
||||||
|
return c.notifiers
|
||||||
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ nav:
|
|||||||
- From binary: install/binary.md
|
- From binary: install/binary.md
|
||||||
- Linux service: install/linux-service.md
|
- Linux service: install/linux-service.md
|
||||||
- Usage:
|
- Usage:
|
||||||
- Command line: usage/cli.md
|
- Command line: usage/command-line.md
|
||||||
- Basic example: usage/basic-example.md
|
- Basic example: usage/basic-example.md
|
||||||
- Configuration:
|
- Configuration:
|
||||||
- Overview: config/index.md
|
- Overview: config/index.md
|
||||||
@@ -125,6 +125,7 @@ nav:
|
|||||||
- FAQ: faq.md
|
- FAQ: faq.md
|
||||||
- Changelog: changelog.md
|
- Changelog: changelog.md
|
||||||
- Migration:
|
- Migration:
|
||||||
|
- Diun v4.0 to v4.17: migration/v4.0-to-v4.17.md
|
||||||
- Diun v3 to v4: migration/v3-to-v4.md
|
- Diun v3 to v4: migration/v3-to-v4.md
|
||||||
- Diun v2 to v3: migration/v2-to-v3.md
|
- Diun v2 to v3: migration/v2-to-v3.md
|
||||||
- Diun v1 to v2: migration/v1-to-v2.md
|
- Diun v1 to v2: migration/v1-to-v2.md
|
||||||
|
|||||||
3
pb/gen.go
Normal file
3
pb/gen.go
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package pb
|
||||||
|
|
||||||
|
//go:generate protoc --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. image.proto notif.proto
|
||||||
764
pb/image.pb.go
Normal file
764
pb/image.pb.go
Normal file
@@ -0,0 +1,764 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.26.0
|
||||||
|
// protoc v3.17.0
|
||||||
|
// source: image.proto
|
||||||
|
|
||||||
|
package pb
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Manifest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||||
|
MimeType string `protobuf:"bytes,2,opt,name=mime_type,json=mimeType,proto3" json:"mime_type,omitempty"`
|
||||||
|
Digest string `protobuf:"bytes,3,opt,name=digest,proto3" json:"digest,omitempty"`
|
||||||
|
Created *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=created,proto3" json:"created,omitempty"`
|
||||||
|
Labels map[string]string `protobuf:"bytes,5,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||||
|
Platform string `protobuf:"bytes,6,opt,name=platform,proto3" json:"platform,omitempty"`
|
||||||
|
Size int64 `protobuf:"varint,7,opt,name=size,proto3" json:"size,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Manifest) Reset() {
|
||||||
|
*x = Manifest{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_image_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Manifest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Manifest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Manifest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_image_proto_msgTypes[0]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Manifest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Manifest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_image_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Manifest) GetTag() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Tag
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Manifest) GetMimeType() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.MimeType
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Manifest) GetDigest() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Digest
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Manifest) GetCreated() *timestamppb.Timestamp {
|
||||||
|
if x != nil {
|
||||||
|
return x.Created
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Manifest) GetLabels() map[string]string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Labels
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Manifest) GetPlatform() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Platform
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Manifest) GetSize() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Size
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageListRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageListRequest) Reset() {
|
||||||
|
*x = ImageListRequest{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_image_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageListRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ImageListRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ImageListRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_image_proto_msgTypes[1]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ImageListRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ImageListRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_image_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageListResponse struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Images []*ImageListResponse_Image `protobuf:"bytes,1,rep,name=images,proto3" json:"images,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageListResponse) Reset() {
|
||||||
|
*x = ImageListResponse{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_image_proto_msgTypes[2]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageListResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ImageListResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ImageListResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_image_proto_msgTypes[2]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ImageListResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ImageListResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_image_proto_rawDescGZIP(), []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageListResponse) GetImages() []*ImageListResponse_Image {
|
||||||
|
if x != nil {
|
||||||
|
return x.Images
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageInspectRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageInspectRequest) Reset() {
|
||||||
|
*x = ImageInspectRequest{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_image_proto_msgTypes[3]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageInspectRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ImageInspectRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ImageInspectRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_image_proto_msgTypes[3]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ImageInspectRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ImageInspectRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_image_proto_rawDescGZIP(), []int{3}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageInspectRequest) GetName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageInspectResponse struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Image *ImageInspectResponse_Image `protobuf:"bytes,1,opt,name=image,proto3" json:"image,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageInspectResponse) Reset() {
|
||||||
|
*x = ImageInspectResponse{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_image_proto_msgTypes[4]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageInspectResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ImageInspectResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ImageInspectResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_image_proto_msgTypes[4]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ImageInspectResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ImageInspectResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_image_proto_rawDescGZIP(), []int{4}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageInspectResponse) GetImage() *ImageInspectResponse_Image {
|
||||||
|
if x != nil {
|
||||||
|
return x.Image
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageRemoveRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageRemoveRequest) Reset() {
|
||||||
|
*x = ImageRemoveRequest{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_image_proto_msgTypes[5]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageRemoveRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ImageRemoveRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ImageRemoveRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_image_proto_msgTypes[5]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ImageRemoveRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ImageRemoveRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_image_proto_rawDescGZIP(), []int{5}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageRemoveRequest) GetName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageRemoveResponse struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Manifests []*Manifest `protobuf:"bytes,1,rep,name=manifests,proto3" json:"manifests,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageRemoveResponse) Reset() {
|
||||||
|
*x = ImageRemoveResponse{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_image_proto_msgTypes[6]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageRemoveResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ImageRemoveResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ImageRemoveResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_image_proto_msgTypes[6]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ImageRemoveResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ImageRemoveResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_image_proto_rawDescGZIP(), []int{6}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageRemoveResponse) GetManifests() []*Manifest {
|
||||||
|
if x != nil {
|
||||||
|
return x.Manifests
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageListResponse_Image struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
|
ManifestsCount int64 `protobuf:"varint,2,opt,name=manifestsCount,proto3" json:"manifestsCount,omitempty"`
|
||||||
|
Latest *Manifest `protobuf:"bytes,3,opt,name=latest,proto3" json:"latest,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageListResponse_Image) Reset() {
|
||||||
|
*x = ImageListResponse_Image{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_image_proto_msgTypes[8]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageListResponse_Image) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ImageListResponse_Image) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ImageListResponse_Image) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_image_proto_msgTypes[8]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ImageListResponse_Image.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ImageListResponse_Image) Descriptor() ([]byte, []int) {
|
||||||
|
return file_image_proto_rawDescGZIP(), []int{2, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageListResponse_Image) GetName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageListResponse_Image) GetManifestsCount() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.ManifestsCount
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageListResponse_Image) GetLatest() *Manifest {
|
||||||
|
if x != nil {
|
||||||
|
return x.Latest
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageInspectResponse_Image struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
|
Manifests []*Manifest `protobuf:"bytes,2,rep,name=manifests,proto3" json:"manifests,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageInspectResponse_Image) Reset() {
|
||||||
|
*x = ImageInspectResponse_Image{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_image_proto_msgTypes[9]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageInspectResponse_Image) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ImageInspectResponse_Image) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ImageInspectResponse_Image) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_image_proto_msgTypes[9]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ImageInspectResponse_Image.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ImageInspectResponse_Image) Descriptor() ([]byte, []int) {
|
||||||
|
return file_image_proto_rawDescGZIP(), []int{4, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageInspectResponse_Image) GetName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ImageInspectResponse_Image) GetManifests() []*Manifest {
|
||||||
|
if x != nil {
|
||||||
|
return x.Manifests
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_image_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_image_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x0b, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x70,
|
||||||
|
0x62, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||||
|
0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x22, 0xa4, 0x02, 0x0a, 0x08, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12,
|
||||||
|
0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61,
|
||||||
|
0x67, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02,
|
||||||
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16,
|
||||||
|
0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
|
||||||
|
0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
|
||||||
|
0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||||
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
||||||
|
0x61, 0x6d, 0x70, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x06,
|
||||||
|
0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70,
|
||||||
|
0x62, 0x2e, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c,
|
||||||
|
0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x1a,
|
||||||
|
0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
|
||||||
|
0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69,
|
||||||
|
0x7a, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x1a, 0x39,
|
||||||
|
0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
|
||||||
|
0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
|
||||||
|
0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
|
||||||
|
0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x12, 0x0a, 0x10, 0x49, 0x6d, 0x61,
|
||||||
|
0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xb3, 0x01,
|
||||||
|
0x0a, 0x11, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||||
|
0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20,
|
||||||
|
0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x4c, 0x69,
|
||||||
|
0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65,
|
||||||
|
0x52, 0x06, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x1a, 0x69, 0x0a, 0x05, 0x49, 0x6d, 0x61, 0x67,
|
||||||
|
0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
|
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73,
|
||||||
|
0x74, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6d,
|
||||||
|
0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a,
|
||||||
|
0x06, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e,
|
||||||
|
0x70, 0x62, 0x2e, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x06, 0x6c, 0x61, 0x74,
|
||||||
|
0x65, 0x73, 0x74, 0x22, 0x29, 0x0a, 0x13, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x73, 0x70,
|
||||||
|
0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
|
||||||
|
0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x95,
|
||||||
|
0x01, 0x0a, 0x14, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52,
|
||||||
|
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65,
|
||||||
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x61, 0x67,
|
||||||
|
0x65, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||||
|
0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x1a, 0x47, 0x0a,
|
||||||
|
0x05, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
|
||||||
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x09, 0x6d, 0x61,
|
||||||
|
0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e,
|
||||||
|
0x70, 0x62, 0x2e, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x09, 0x6d, 0x61, 0x6e,
|
||||||
|
0x69, 0x66, 0x65, 0x73, 0x74, 0x73, 0x22, 0x28, 0x0a, 0x12, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52,
|
||||||
|
0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04,
|
||||||
|
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
|
||||||
|
0x22, 0x41, 0x0a, 0x13, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52,
|
||||||
|
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x09, 0x6d, 0x61, 0x6e, 0x69, 0x66,
|
||||||
|
0x65, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e,
|
||||||
|
0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x09, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65,
|
||||||
|
0x73, 0x74, 0x73, 0x32, 0xd1, 0x01, 0x0a, 0x0c, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x53, 0x65, 0x72,
|
||||||
|
0x76, 0x69, 0x63, 0x65, 0x12, 0x3a, 0x0a, 0x09, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73,
|
||||||
|
0x74, 0x12, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74,
|
||||||
|
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x61,
|
||||||
|
0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
|
||||||
|
0x12, 0x43, 0x0a, 0x0c, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74,
|
||||||
|
0x12, 0x17, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x73, 0x70, 0x65,
|
||||||
|
0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x49,
|
||||||
|
0x6d, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||||
|
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x0b, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65,
|
||||||
|
0x6d, 0x6f, 0x76, 0x65, 0x12, 0x16, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52,
|
||||||
|
0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x70,
|
||||||
|
0x62, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73,
|
||||||
|
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x1e, 0x5a, 0x1c, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||||
|
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x61, 0x7a, 0x79, 0x2d, 0x6d, 0x61, 0x78, 0x2f,
|
||||||
|
0x64, 0x69, 0x75, 0x6e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_image_proto_rawDescOnce sync.Once
|
||||||
|
file_image_proto_rawDescData = file_image_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_image_proto_rawDescGZIP() []byte {
|
||||||
|
file_image_proto_rawDescOnce.Do(func() {
|
||||||
|
file_image_proto_rawDescData = protoimpl.X.CompressGZIP(file_image_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_image_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_image_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
|
||||||
|
var file_image_proto_goTypes = []interface{}{
|
||||||
|
(*Manifest)(nil), // 0: pb.Manifest
|
||||||
|
(*ImageListRequest)(nil), // 1: pb.ImageListRequest
|
||||||
|
(*ImageListResponse)(nil), // 2: pb.ImageListResponse
|
||||||
|
(*ImageInspectRequest)(nil), // 3: pb.ImageInspectRequest
|
||||||
|
(*ImageInspectResponse)(nil), // 4: pb.ImageInspectResponse
|
||||||
|
(*ImageRemoveRequest)(nil), // 5: pb.ImageRemoveRequest
|
||||||
|
(*ImageRemoveResponse)(nil), // 6: pb.ImageRemoveResponse
|
||||||
|
nil, // 7: pb.Manifest.LabelsEntry
|
||||||
|
(*ImageListResponse_Image)(nil), // 8: pb.ImageListResponse.Image
|
||||||
|
(*ImageInspectResponse_Image)(nil), // 9: pb.ImageInspectResponse.Image
|
||||||
|
(*timestamppb.Timestamp)(nil), // 10: google.protobuf.Timestamp
|
||||||
|
}
|
||||||
|
var file_image_proto_depIdxs = []int32{
|
||||||
|
10, // 0: pb.Manifest.created:type_name -> google.protobuf.Timestamp
|
||||||
|
7, // 1: pb.Manifest.labels:type_name -> pb.Manifest.LabelsEntry
|
||||||
|
8, // 2: pb.ImageListResponse.images:type_name -> pb.ImageListResponse.Image
|
||||||
|
9, // 3: pb.ImageInspectResponse.image:type_name -> pb.ImageInspectResponse.Image
|
||||||
|
0, // 4: pb.ImageRemoveResponse.manifests:type_name -> pb.Manifest
|
||||||
|
0, // 5: pb.ImageListResponse.Image.latest:type_name -> pb.Manifest
|
||||||
|
0, // 6: pb.ImageInspectResponse.Image.manifests:type_name -> pb.Manifest
|
||||||
|
1, // 7: pb.ImageService.ImageList:input_type -> pb.ImageListRequest
|
||||||
|
3, // 8: pb.ImageService.ImageInspect:input_type -> pb.ImageInspectRequest
|
||||||
|
5, // 9: pb.ImageService.ImageRemove:input_type -> pb.ImageRemoveRequest
|
||||||
|
2, // 10: pb.ImageService.ImageList:output_type -> pb.ImageListResponse
|
||||||
|
4, // 11: pb.ImageService.ImageInspect:output_type -> pb.ImageInspectResponse
|
||||||
|
6, // 12: pb.ImageService.ImageRemove:output_type -> pb.ImageRemoveResponse
|
||||||
|
10, // [10:13] is the sub-list for method output_type
|
||||||
|
7, // [7:10] is the sub-list for method input_type
|
||||||
|
7, // [7:7] is the sub-list for extension type_name
|
||||||
|
7, // [7:7] is the sub-list for extension extendee
|
||||||
|
0, // [0:7] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_image_proto_init() }
|
||||||
|
func file_image_proto_init() {
|
||||||
|
if File_image_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_image_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*Manifest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_image_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*ImageListRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_image_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*ImageListResponse); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_image_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*ImageInspectRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_image_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*ImageInspectResponse); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_image_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*ImageRemoveRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_image_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*ImageRemoveResponse); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_image_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*ImageListResponse_Image); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_image_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*ImageInspectResponse_Image); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_image_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 10,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 1,
|
||||||
|
},
|
||||||
|
GoTypes: file_image_proto_goTypes,
|
||||||
|
DependencyIndexes: file_image_proto_depIdxs,
|
||||||
|
MessageInfos: file_image_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_image_proto = out.File
|
||||||
|
file_image_proto_rawDesc = nil
|
||||||
|
file_image_proto_goTypes = nil
|
||||||
|
file_image_proto_depIdxs = nil
|
||||||
|
}
|
||||||
53
pb/image.proto
Normal file
53
pb/image.proto
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
option go_package = "github.com/crazy-max/diun/pb";
|
||||||
|
|
||||||
|
package pb;
|
||||||
|
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
|
message Manifest {
|
||||||
|
string tag = 1;
|
||||||
|
string mime_type = 2;
|
||||||
|
string digest = 3;
|
||||||
|
google.protobuf.Timestamp created = 4;
|
||||||
|
map<string, string> labels = 5;
|
||||||
|
string platform = 6;
|
||||||
|
int64 size = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ImageListRequest {}
|
||||||
|
|
||||||
|
message ImageListResponse {
|
||||||
|
message Image {
|
||||||
|
string name = 1;
|
||||||
|
int64 manifestsCount = 2;
|
||||||
|
Manifest latest = 3;
|
||||||
|
}
|
||||||
|
repeated Image images = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ImageInspectRequest {
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ImageInspectResponse {
|
||||||
|
message Image {
|
||||||
|
string name = 1;
|
||||||
|
repeated Manifest manifests = 2;
|
||||||
|
}
|
||||||
|
Image image = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ImageRemoveRequest {
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ImageRemoveResponse {
|
||||||
|
repeated Manifest manifests = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
service ImageService {
|
||||||
|
rpc ImageList(ImageListRequest) returns (ImageListResponse) {}
|
||||||
|
rpc ImageInspect(ImageInspectRequest) returns (ImageInspectResponse) {}
|
||||||
|
rpc ImageRemove(ImageRemoveRequest) returns (ImageRemoveResponse) {}
|
||||||
|
}
|
||||||
173
pb/image_grpc.pb.go
Normal file
173
pb/image_grpc.pb.go
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
|
|
||||||
|
package pb
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
// Requires gRPC-Go v1.32.0 or later.
|
||||||
|
const _ = grpc.SupportPackageIsVersion7
|
||||||
|
|
||||||
|
// ImageServiceClient is the client API for ImageService service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
type ImageServiceClient interface {
|
||||||
|
ImageList(ctx context.Context, in *ImageListRequest, opts ...grpc.CallOption) (*ImageListResponse, error)
|
||||||
|
ImageInspect(ctx context.Context, in *ImageInspectRequest, opts ...grpc.CallOption) (*ImageInspectResponse, error)
|
||||||
|
ImageRemove(ctx context.Context, in *ImageRemoveRequest, opts ...grpc.CallOption) (*ImageRemoveResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type imageServiceClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewImageServiceClient(cc grpc.ClientConnInterface) ImageServiceClient {
|
||||||
|
return &imageServiceClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *imageServiceClient) ImageList(ctx context.Context, in *ImageListRequest, opts ...grpc.CallOption) (*ImageListResponse, error) {
|
||||||
|
out := new(ImageListResponse)
|
||||||
|
err := c.cc.Invoke(ctx, "/pb.ImageService/ImageList", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *imageServiceClient) ImageInspect(ctx context.Context, in *ImageInspectRequest, opts ...grpc.CallOption) (*ImageInspectResponse, error) {
|
||||||
|
out := new(ImageInspectResponse)
|
||||||
|
err := c.cc.Invoke(ctx, "/pb.ImageService/ImageInspect", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *imageServiceClient) ImageRemove(ctx context.Context, in *ImageRemoveRequest, opts ...grpc.CallOption) (*ImageRemoveResponse, error) {
|
||||||
|
out := new(ImageRemoveResponse)
|
||||||
|
err := c.cc.Invoke(ctx, "/pb.ImageService/ImageRemove", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageServiceServer is the server API for ImageService service.
|
||||||
|
// All implementations must embed UnimplementedImageServiceServer
|
||||||
|
// for forward compatibility
|
||||||
|
type ImageServiceServer interface {
|
||||||
|
ImageList(context.Context, *ImageListRequest) (*ImageListResponse, error)
|
||||||
|
ImageInspect(context.Context, *ImageInspectRequest) (*ImageInspectResponse, error)
|
||||||
|
ImageRemove(context.Context, *ImageRemoveRequest) (*ImageRemoveResponse, error)
|
||||||
|
mustEmbedUnimplementedImageServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedImageServiceServer must be embedded to have forward compatible implementations.
|
||||||
|
type UnimplementedImageServiceServer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedImageServiceServer) ImageList(context.Context, *ImageListRequest) (*ImageListResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method ImageList not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedImageServiceServer) ImageInspect(context.Context, *ImageInspectRequest) (*ImageInspectResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method ImageInspect not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedImageServiceServer) ImageRemove(context.Context, *ImageRemoveRequest) (*ImageRemoveResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method ImageRemove not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedImageServiceServer) mustEmbedUnimplementedImageServiceServer() {}
|
||||||
|
|
||||||
|
// UnsafeImageServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to ImageServiceServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeImageServiceServer interface {
|
||||||
|
mustEmbedUnimplementedImageServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterImageServiceServer(s grpc.ServiceRegistrar, srv ImageServiceServer) {
|
||||||
|
s.RegisterService(&ImageService_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _ImageService_ImageList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(ImageListRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(ImageServiceServer).ImageList(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/pb.ImageService/ImageList",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(ImageServiceServer).ImageList(ctx, req.(*ImageListRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _ImageService_ImageInspect_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(ImageInspectRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(ImageServiceServer).ImageInspect(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/pb.ImageService/ImageInspect",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(ImageServiceServer).ImageInspect(ctx, req.(*ImageInspectRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _ImageService_ImageRemove_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(ImageRemoveRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(ImageServiceServer).ImageRemove(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/pb.ImageService/ImageRemove",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(ImageServiceServer).ImageRemove(ctx, req.(*ImageRemoveRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageService_ServiceDesc is the grpc.ServiceDesc for ImageService service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var ImageService_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "pb.ImageService",
|
||||||
|
HandlerType: (*ImageServiceServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "ImageList",
|
||||||
|
Handler: _ImageService_ImageList_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "ImageInspect",
|
||||||
|
Handler: _ImageService_ImageInspect_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "ImageRemove",
|
||||||
|
Handler: _ImageService_ImageRemove_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{},
|
||||||
|
Metadata: "image.proto",
|
||||||
|
}
|
||||||
202
pb/notif.pb.go
Normal file
202
pb/notif.pb.go
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.26.0
|
||||||
|
// protoc v3.17.0
|
||||||
|
// source: notif.proto
|
||||||
|
|
||||||
|
package pb
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type NotifTestRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *NotifTestRequest) Reset() {
|
||||||
|
*x = NotifTestRequest{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_notif_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *NotifTestRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*NotifTestRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *NotifTestRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_notif_proto_msgTypes[0]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NotifTestRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*NotifTestRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_notif_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
type NotifTestResponse struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *NotifTestResponse) Reset() {
|
||||||
|
*x = NotifTestResponse{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_notif_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *NotifTestResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*NotifTestResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *NotifTestResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_notif_proto_msgTypes[1]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NotifTestResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*NotifTestResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_notif_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *NotifTestResponse) GetMessage() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Message
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_notif_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_notif_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x0b, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x70,
|
||||||
|
0x62, 0x22, 0x12, 0x0a, 0x10, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65,
|
||||||
|
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x2d, 0x0a, 0x11, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x54, 0x65,
|
||||||
|
0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65,
|
||||||
|
0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73,
|
||||||
|
0x73, 0x61, 0x67, 0x65, 0x32, 0x4a, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x53, 0x65, 0x72,
|
||||||
|
0x76, 0x69, 0x63, 0x65, 0x12, 0x3a, 0x0a, 0x09, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x54, 0x65, 0x73,
|
||||||
|
0x74, 0x12, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x54, 0x65, 0x73, 0x74,
|
||||||
|
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x4e, 0x6f, 0x74,
|
||||||
|
0x69, 0x66, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
|
||||||
|
0x42, 0x1e, 0x5a, 0x1c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
|
||||||
|
0x72, 0x61, 0x7a, 0x79, 0x2d, 0x6d, 0x61, 0x78, 0x2f, 0x64, 0x69, 0x75, 0x6e, 0x2f, 0x70, 0x62,
|
||||||
|
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_notif_proto_rawDescOnce sync.Once
|
||||||
|
file_notif_proto_rawDescData = file_notif_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_notif_proto_rawDescGZIP() []byte {
|
||||||
|
file_notif_proto_rawDescOnce.Do(func() {
|
||||||
|
file_notif_proto_rawDescData = protoimpl.X.CompressGZIP(file_notif_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_notif_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_notif_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||||
|
var file_notif_proto_goTypes = []interface{}{
|
||||||
|
(*NotifTestRequest)(nil), // 0: pb.NotifTestRequest
|
||||||
|
(*NotifTestResponse)(nil), // 1: pb.NotifTestResponse
|
||||||
|
}
|
||||||
|
var file_notif_proto_depIdxs = []int32{
|
||||||
|
0, // 0: pb.NotifService.NotifTest:input_type -> pb.NotifTestRequest
|
||||||
|
1, // 1: pb.NotifService.NotifTest:output_type -> pb.NotifTestResponse
|
||||||
|
1, // [1:2] is the sub-list for method output_type
|
||||||
|
0, // [0:1] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_notif_proto_init() }
|
||||||
|
func file_notif_proto_init() {
|
||||||
|
if File_notif_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_notif_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*NotifTestRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_notif_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*NotifTestResponse); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_notif_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 2,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 1,
|
||||||
|
},
|
||||||
|
GoTypes: file_notif_proto_goTypes,
|
||||||
|
DependencyIndexes: file_notif_proto_depIdxs,
|
||||||
|
MessageInfos: file_notif_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_notif_proto = out.File
|
||||||
|
file_notif_proto_rawDesc = nil
|
||||||
|
file_notif_proto_goTypes = nil
|
||||||
|
file_notif_proto_depIdxs = nil
|
||||||
|
}
|
||||||
14
pb/notif.proto
Normal file
14
pb/notif.proto
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
option go_package = "github.com/crazy-max/diun/pb";
|
||||||
|
|
||||||
|
package pb;
|
||||||
|
|
||||||
|
message NotifTestRequest {}
|
||||||
|
|
||||||
|
message NotifTestResponse {
|
||||||
|
string message = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
service NotifService {
|
||||||
|
rpc NotifTest(NotifTestRequest) returns (NotifTestResponse) {}
|
||||||
|
}
|
||||||
101
pb/notif_grpc.pb.go
Normal file
101
pb/notif_grpc.pb.go
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
|
|
||||||
|
package pb
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
// Requires gRPC-Go v1.32.0 or later.
|
||||||
|
const _ = grpc.SupportPackageIsVersion7
|
||||||
|
|
||||||
|
// NotifServiceClient is the client API for NotifService service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
type NotifServiceClient interface {
|
||||||
|
NotifTest(ctx context.Context, in *NotifTestRequest, opts ...grpc.CallOption) (*NotifTestResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type notifServiceClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNotifServiceClient(cc grpc.ClientConnInterface) NotifServiceClient {
|
||||||
|
return ¬ifServiceClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *notifServiceClient) NotifTest(ctx context.Context, in *NotifTestRequest, opts ...grpc.CallOption) (*NotifTestResponse, error) {
|
||||||
|
out := new(NotifTestResponse)
|
||||||
|
err := c.cc.Invoke(ctx, "/pb.NotifService/NotifTest", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifServiceServer is the server API for NotifService service.
|
||||||
|
// All implementations must embed UnimplementedNotifServiceServer
|
||||||
|
// for forward compatibility
|
||||||
|
type NotifServiceServer interface {
|
||||||
|
NotifTest(context.Context, *NotifTestRequest) (*NotifTestResponse, error)
|
||||||
|
mustEmbedUnimplementedNotifServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedNotifServiceServer must be embedded to have forward compatible implementations.
|
||||||
|
type UnimplementedNotifServiceServer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedNotifServiceServer) NotifTest(context.Context, *NotifTestRequest) (*NotifTestResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method NotifTest not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedNotifServiceServer) mustEmbedUnimplementedNotifServiceServer() {}
|
||||||
|
|
||||||
|
// UnsafeNotifServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to NotifServiceServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeNotifServiceServer interface {
|
||||||
|
mustEmbedUnimplementedNotifServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterNotifServiceServer(s grpc.ServiceRegistrar, srv NotifServiceServer) {
|
||||||
|
s.RegisterService(&NotifService_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _NotifService_NotifTest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(NotifTestRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(NotifServiceServer).NotifTest(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/pb.NotifService/NotifTest",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(NotifServiceServer).NotifTest(ctx, req.(*NotifTestRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifService_ServiceDesc is the grpc.ServiceDesc for NotifService service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var NotifService_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "pb.NotifService",
|
||||||
|
HandlerType: (*NotifServiceServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "NotifTest",
|
||||||
|
Handler: _NotifService_NotifTest_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{},
|
||||||
|
Metadata: "notif.proto",
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user