mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
Simplify node lock and various small changes
Co-authored-by: Fabian Kammel <fabian@kammel.dev> Co-authored-by: Daniel Weiße <66256922+daniel-weisse@users.noreply.github.com>
This commit is contained in:
parent
2bcf001d52
commit
cce2611e2a
@ -22,6 +22,10 @@ inputs:
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Determine pseudo version
|
||||
id: pseudo-version
|
||||
uses: ./.github/actions/pseudo_version
|
||||
|
||||
- name: Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
@ -31,6 +35,7 @@ runs:
|
||||
tags: |
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
type=raw,value=${{ inputs.pushTag }},enable=${{ '' != inputs.pushTag }}
|
||||
type=raw,value=${{ steps.pseudo-version.outputs.pseudo-version }},enable=${{ '' != steps.pseudo-version.outputs.pseudo-version }}
|
||||
type=ref,event=branch
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
|
26
.github/actions/pseudo_version/action.yml
vendored
Normal file
26
.github/actions/pseudo_version/action.yml
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
name: Determine pseudo version
|
||||
description: "Determine go-like pseudo version to use as container image tag."
|
||||
outputs:
|
||||
pseudo-version:
|
||||
description: "Pseudo version based on the current HEAD"
|
||||
value: ${{ steps.pseudo-version.outputs.pseudo-version }}
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: "1.18"
|
||||
- name: get pseudo version
|
||||
id: pseudo-version
|
||||
run: |
|
||||
set -e
|
||||
set -o pipefail
|
||||
if $(git rev-parse --is-shallow-repository); then
|
||||
git fetch --prune --unshallow --tags -v
|
||||
else
|
||||
git fetch --tags -v
|
||||
fi
|
||||
echo "::set-output name=pseudo-version::$(go run .)"
|
||||
working-directory: hack/pseudo-version
|
||||
shell: bash
|
@ -48,7 +48,7 @@ jobs:
|
||||
echo "microServiceDockerfile=verify/Dockerfile" >> $GITHUB_ENV ;;
|
||||
esac
|
||||
|
||||
- name: Build and upload join-service container image
|
||||
- name: Build and upload container image
|
||||
id: build-and-upload
|
||||
uses: ./.github/actions/build_micro_service
|
||||
with:
|
||||
|
@ -19,10 +19,9 @@ calling the InitCluster function of our Kubernetes library, which does a `kubead
|
||||
|
||||
## Join Flow
|
||||
|
||||
The JoinClient is a gRPC client that is trying to connect to an JoinService, which might be running
|
||||
in an already existing cluster as DaemonSet. The JoinService is validating the instance which wants to join the cluster using
|
||||
aTLS. For details on the used protocol and the verification of a joining instances measurements, see the
|
||||
[joinservice](./../joinservice) package.
|
||||
The JoinClient is a gRPC client that tries to connect to a JoinService of an already existing cluster.
|
||||
The JoinService validates the instance using [aTLS](./../internal/atls/README.md).
|
||||
For details on the used protocol, see the [joinservice](./../joinservice) package.
|
||||
|
||||
If the JoinService successfully verifies the instance, it issues a join ticket. The JoinClient then
|
||||
joins the cluster by calling the `kubeadm join` command, using the token and other needed information
|
||||
|
@ -33,13 +33,12 @@ import (
|
||||
const (
|
||||
defaultIP = "0.0.0.0"
|
||||
defaultPort = "9000"
|
||||
// ConstellationCSP is the Cloud Service Provider Constellation is running on.
|
||||
// ConstellationCSP is the environment variable stating which Cloud Service Provider Constellation is running on.
|
||||
constellationCSP = "CONSTEL_CSP"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
var bindIP, bindPort string
|
||||
|
@ -3,7 +3,7 @@ package main
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/edgelesssys/constellation/bootstrapper/internal/exit"
|
||||
"github.com/edgelesssys/constellation/bootstrapper/internal/clean"
|
||||
"github.com/edgelesssys/constellation/bootstrapper/internal/initserver"
|
||||
"github.com/edgelesssys/constellation/bootstrapper/internal/joinclient"
|
||||
"github.com/edgelesssys/constellation/bootstrapper/internal/logging"
|
||||
@ -46,22 +46,14 @@ func run(issuer quoteIssuer, tpm vtpm.TPMOpenFunc, fileHandler file.Handler,
|
||||
dialer := dialer.New(issuer, nil, &net.Dialer{})
|
||||
joinClient := joinclient.New(nodeLock, dialer, kube, metadata, logger)
|
||||
|
||||
cleaner := exit.New().
|
||||
With(initServer).
|
||||
With(joinClient)
|
||||
cleaner := clean.New().With(initServer).With(joinClient)
|
||||
go cleaner.Start()
|
||||
defer cleaner.Done()
|
||||
|
||||
joinClient.Start(cleaner)
|
||||
|
||||
if err := initServer.Serve(bindIP, bindPort, cleaner); err != nil {
|
||||
logger.Error("Failed to serve init server", zap.Error(err))
|
||||
}
|
||||
|
||||
// wait for join client and server to exit cleanly
|
||||
cleaner.Clean()
|
||||
|
||||
// if node lock was never acquired, then we didn't bootstrap successfully.
|
||||
if !nodeLock.Locked() {
|
||||
cloudLogger.Disclose("bootstrapper failed")
|
||||
logger.Fatal("bootstrapper failed")
|
||||
logger.Fatal("Failed to serve init server", zap.Error(err))
|
||||
}
|
||||
|
||||
logger.Info("bootstrapper done")
|
||||
|
64
bootstrapper/internal/clean/clean.go
Normal file
64
bootstrapper/internal/clean/clean.go
Normal file
@ -0,0 +1,64 @@
|
||||
package clean
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type cleaner struct {
|
||||
stoppers []stopper
|
||||
stopC chan struct{}
|
||||
startOnce sync.Once
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
// New creates a new cleaner.
|
||||
func New(stoppers ...stopper) *cleaner {
|
||||
res := &cleaner{
|
||||
stoppers: stoppers,
|
||||
stopC: make(chan struct{}, 1),
|
||||
}
|
||||
res.wg.Add(1) // for the Start goroutine
|
||||
return res
|
||||
}
|
||||
|
||||
// With adds a new stopper to the cleaner.
|
||||
func (c *cleaner) With(stopper stopper) *cleaner {
|
||||
c.stoppers = append(c.stoppers, stopper)
|
||||
return c
|
||||
}
|
||||
|
||||
// Start blocks until it receives a stop message, stops all services gracefully and returns.
|
||||
func (c *cleaner) Start() {
|
||||
c.startOnce.Do(func() {
|
||||
defer c.wg.Done()
|
||||
// wait for the stop message
|
||||
<-c.stopC
|
||||
|
||||
c.wg.Add(len(c.stoppers))
|
||||
for _, stopItem := range c.stoppers {
|
||||
go func(stopItem stopper) {
|
||||
defer c.wg.Done()
|
||||
stopItem.Stop()
|
||||
}(stopItem)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Clean initiates the cleanup but does not wait for it to complete.
|
||||
func (c *cleaner) Clean() {
|
||||
// try to enqueue the stop message once
|
||||
// if the channel is full, the message is dropped
|
||||
select {
|
||||
case c.stopC <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// Done waits for the cleanup to complete.
|
||||
func (c *cleaner) Done() {
|
||||
c.wg.Wait()
|
||||
}
|
||||
|
||||
type stopper interface {
|
||||
Stop()
|
||||
}
|
98
bootstrapper/internal/clean/clean_test.go
Normal file
98
bootstrapper/internal/clean/clean_test.go
Normal file
@ -0,0 +1,98 @@
|
||||
package clean
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/goleak"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
goleak.VerifyTestMain(m)
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cleaner := New(&spyStopper{})
|
||||
assert.NotNil(cleaner)
|
||||
assert.NotEmpty(cleaner.stoppers)
|
||||
}
|
||||
|
||||
func TestWith(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cleaner := New().With(&spyStopper{})
|
||||
assert.NotEmpty(cleaner.stoppers)
|
||||
}
|
||||
|
||||
func TestClean(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
stopper := &spyStopper{}
|
||||
cleaner := New(stopper)
|
||||
go cleaner.Start()
|
||||
cleaner.Clean()
|
||||
cleaner.Done()
|
||||
assert.Equal(int64(1), atomic.LoadInt64(&stopper.stopped))
|
||||
// call again to make sure it doesn't panic or block or clean up again
|
||||
cleaner.Clean()
|
||||
assert.Equal(int64(1), atomic.LoadInt64(&stopper.stopped))
|
||||
}
|
||||
|
||||
func TestCleanBeforeStart(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// calling Clean before Start should work
|
||||
stopper := &spyStopper{}
|
||||
cleaner := New(stopper)
|
||||
cleaner.Clean()
|
||||
cleaner.Start()
|
||||
cleaner.Done()
|
||||
assert.Equal(int64(1), atomic.LoadInt64(&stopper.stopped))
|
||||
}
|
||||
|
||||
func TestConcurrent(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// calling Clean concurrently should call Stop exactly once
|
||||
|
||||
stopper := &spyStopper{}
|
||||
cleaner := New(stopper)
|
||||
|
||||
parallelism := 10
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
start := func() {
|
||||
defer wg.Done()
|
||||
cleaner.Start()
|
||||
}
|
||||
|
||||
clean := func() {
|
||||
defer wg.Done()
|
||||
cleaner.Clean()
|
||||
}
|
||||
|
||||
done := func() {
|
||||
defer wg.Done()
|
||||
cleaner.Done()
|
||||
}
|
||||
|
||||
wg.Add(3 * parallelism)
|
||||
for i := 0; i < parallelism; i++ {
|
||||
go start()
|
||||
go clean()
|
||||
go done()
|
||||
}
|
||||
wg.Wait()
|
||||
cleaner.Done()
|
||||
assert.Equal(int64(1), atomic.LoadInt64(&stopper.stopped))
|
||||
}
|
||||
|
||||
type spyStopper struct {
|
||||
stopped int64
|
||||
}
|
||||
|
||||
func (s *spyStopper) Stop() {
|
||||
atomic.AddInt64(&s.stopped, 1)
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package exit
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type cleaner struct {
|
||||
stoppers []stopper
|
||||
|
||||
cleanupDone bool
|
||||
wg sync.WaitGroup
|
||||
mux sync.Mutex
|
||||
}
|
||||
|
||||
// New creates a new cleaner.
|
||||
func New(stoppers ...stopper) *cleaner {
|
||||
return &cleaner{
|
||||
stoppers: stoppers,
|
||||
}
|
||||
}
|
||||
|
||||
// With adds a new stopper to the cleaner.
|
||||
func (c *cleaner) With(stopper stopper) *cleaner {
|
||||
c.stoppers = append(c.stoppers, stopper)
|
||||
return c
|
||||
}
|
||||
|
||||
// Clean stops all services gracefully.
|
||||
func (c *cleaner) Clean() {
|
||||
// only cleanup once
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
if c.cleanupDone {
|
||||
return
|
||||
}
|
||||
|
||||
c.wg.Add(len(c.stoppers))
|
||||
for _, stopItem := range c.stoppers {
|
||||
go func(stopItem stopper) {
|
||||
stopItem.Stop()
|
||||
c.wg.Done()
|
||||
}(stopItem)
|
||||
}
|
||||
c.wg.Wait()
|
||||
c.cleanupDone = true
|
||||
}
|
||||
|
||||
type stopper interface {
|
||||
Stop()
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package exit
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/goleak"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
goleak.VerifyTestMain(m)
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cleaner := New(&spyStopper{})
|
||||
assert.NotNil(cleaner)
|
||||
assert.NotEmpty(cleaner.stoppers)
|
||||
}
|
||||
|
||||
func TestWith(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cleaner := New().With(&spyStopper{})
|
||||
assert.NotEmpty(cleaner.stoppers)
|
||||
}
|
||||
|
||||
func TestClean(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
stopper := &spyStopper{}
|
||||
cleaner := New(stopper)
|
||||
cleaner.Clean()
|
||||
assert.True(stopper.stopped)
|
||||
|
||||
// call again to make sure it doesn't panic or block
|
||||
cleaner.Clean()
|
||||
}
|
||||
|
||||
type spyStopper struct {
|
||||
stopped bool
|
||||
}
|
||||
|
||||
func (s *spyStopper) Stop() {
|
||||
s.stopped = true
|
||||
}
|
@ -35,6 +35,7 @@ type Server struct {
|
||||
disk encryptedDisk
|
||||
fileHandler file.Handler
|
||||
grpcServer serveStopper
|
||||
cleaner cleaner
|
||||
|
||||
logger *zap.Logger
|
||||
|
||||
@ -70,18 +71,17 @@ func New(lock locker, kube ClusterInitializer, issuer atls.Issuer, fh file.Handl
|
||||
|
||||
// Serve starts the initialization server.
|
||||
func (s *Server) Serve(ip, port string, cleaner cleaner) error {
|
||||
s.cleaner = cleaner
|
||||
lis, err := net.Listen("tcp", net.JoinHostPort(ip, port))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to listen: %w", err)
|
||||
}
|
||||
|
||||
err = s.grpcServer.Serve(lis)
|
||||
cleaner.Clean()
|
||||
return err
|
||||
return s.grpcServer.Serve(lis)
|
||||
}
|
||||
|
||||
// Init initializes the cluster.
|
||||
func (s *Server) Init(ctx context.Context, req *initproto.InitRequest) (*initproto.InitResponse, error) {
|
||||
defer s.cleaner.Clean()
|
||||
s.logger.Info("Init called")
|
||||
|
||||
id, err := s.deriveAttestationID(req.MasterSecret)
|
||||
@ -99,7 +99,6 @@ func (s *Server) Init(ctx context.Context, req *initproto.InitRequest) (*initpro
|
||||
// init does not make sense, so we just stop.
|
||||
//
|
||||
// The server stops itself after the current call is done.
|
||||
go s.grpcServer.GracefulStop()
|
||||
s.logger.Info("node is already in a join process")
|
||||
return nil, status.Error(codes.FailedPrecondition, "node is already being activated")
|
||||
}
|
||||
@ -137,7 +136,6 @@ func (s *Server) Init(ctx context.Context, req *initproto.InitRequest) (*initpro
|
||||
}
|
||||
|
||||
s.logger.Info("Init succeeded")
|
||||
go s.grpcServer.GracefulStop()
|
||||
return &initproto.InitResponse{
|
||||
Kubeconfig: kubeconfig,
|
||||
OwnerId: id.Owner,
|
||||
|
@ -124,6 +124,7 @@ func TestInit(t *testing.T) {
|
||||
fileHandler: tc.fileHandler,
|
||||
logger: zaptest.NewLogger(t),
|
||||
grpcServer: serveStopper,
|
||||
cleaner: &fakeCleaner{serveStopper: serveStopper},
|
||||
}
|
||||
|
||||
kubeconfig, err := server.Init(context.Background(), tc.req)
|
||||
@ -253,3 +254,11 @@ func newFakeLock() *fakeLock {
|
||||
func (l *fakeLock) TryLockOnce(_, _ []byte) (bool, error) {
|
||||
return l.state.TryLock(), nil
|
||||
}
|
||||
|
||||
type fakeCleaner struct {
|
||||
serveStopper
|
||||
}
|
||||
|
||||
func (f *fakeCleaner) Clean() {
|
||||
go f.serveStopper.GracefulStop() // this is not the correct way to do this, but it's fine for testing
|
||||
}
|
||||
|
@ -96,6 +96,7 @@ func (c *JoinClient) Start(cleaner cleaner) {
|
||||
defer ticker.Stop()
|
||||
defer func() { c.stopDone <- struct{}{} }()
|
||||
defer c.log.Info("Client stopped")
|
||||
defer cleaner.Clean()
|
||||
|
||||
diskUUID, err := c.getDiskUUID()
|
||||
if err != nil {
|
||||
@ -124,7 +125,6 @@ func (c *JoinClient) Start(cleaner cleaner) {
|
||||
err := c.tryJoinWithAvailableServices()
|
||||
if err == nil {
|
||||
c.log.Info("Joined successfully. Client is shut down.")
|
||||
go cleaner.Clean()
|
||||
return
|
||||
} else if isUnrecoverable(err) {
|
||||
c.log.Error("Unrecoverable error occurred", zap.Error(err))
|
||||
|
@ -11,6 +11,9 @@ type gcpGuestAgentDaemonset struct {
|
||||
DaemonSet apps.DaemonSet
|
||||
}
|
||||
|
||||
// NewGCPGuestAgentDaemonset creates a new GCP Guest Agent Daemonset.
|
||||
// The GCP guest agent is built in a separate repository: https://github.com/edgelesssys/gcp-guest-agent
|
||||
// It is used automatically to add loadbalancer IPs to the local routing table of GCP instances.
|
||||
func NewGCPGuestAgentDaemonset() *gcpGuestAgentDaemonset {
|
||||
return &gcpGuestAgentDaemonset{
|
||||
DaemonSet: apps.DaemonSet{
|
||||
@ -61,7 +64,7 @@ func NewGCPGuestAgentDaemonset() *gcpGuestAgentDaemonset {
|
||||
Containers: []k8s.Container{
|
||||
{
|
||||
Name: "gcp-guest-agent",
|
||||
Image: gcpGuestImage,
|
||||
Image: gcpGuestImage, // built from https://github.com/edgelesssys/gcp-guest-agent
|
||||
SecurityContext: &k8s.SecurityContext{
|
||||
Privileged: func(b bool) *bool { return &b }(true),
|
||||
Capabilities: &k8s.Capabilities{
|
||||
|
@ -338,8 +338,8 @@ func manuallySetLoadbalancerIP(ctx context.Context, ip string) error {
|
||||
if !strings.Contains(ip, "/") {
|
||||
ip = ip + "/32"
|
||||
}
|
||||
args := fmt.Sprintf("route add to local %s scope host dev ens3 proto 66", ip)
|
||||
_, err := exec.CommandContext(ctx, "ip", strings.Split(args, " ")...).Output()
|
||||
args := []string{"route", "add", "to", "local", ip, "scope", "host", "dev", "ens3", "proto", "66"}
|
||||
_, err := exec.CommandContext(ctx, "ip", args...).Output()
|
||||
if err != nil {
|
||||
var exitErr *exec.ExitError
|
||||
if errors.As(err, &exitErr) {
|
||||
|
@ -14,39 +14,23 @@ import (
|
||||
// There is no way to unlock, so the state changes only once from unlock to
|
||||
// locked.
|
||||
type Lock struct {
|
||||
tpm vtpm.TPMOpenFunc
|
||||
locked bool
|
||||
state *sync.Mutex
|
||||
mux *sync.RWMutex
|
||||
tpm vtpm.TPMOpenFunc
|
||||
mux *sync.Mutex
|
||||
}
|
||||
|
||||
// New creates a new NodeLock, which is unlocked.
|
||||
func New(tpm vtpm.TPMOpenFunc) *Lock {
|
||||
return &Lock{
|
||||
tpm: tpm,
|
||||
state: &sync.Mutex{},
|
||||
mux: &sync.RWMutex{},
|
||||
tpm: tpm,
|
||||
mux: &sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
// TryLockOnce tries to lock the node. If the node is already locked, it
|
||||
// returns false. If the node is unlocked, it locks it and returns true.
|
||||
func (l *Lock) TryLockOnce(ownerID, clusterID []byte) (bool, error) {
|
||||
success := l.state.TryLock()
|
||||
if success {
|
||||
l.mux.Lock()
|
||||
defer l.mux.Unlock()
|
||||
l.locked = true
|
||||
if err := vtpm.MarkNodeAsBootstrapped(l.tpm, ownerID, clusterID); err != nil {
|
||||
return success, err
|
||||
}
|
||||
if !l.mux.TryLock() {
|
||||
return false, nil
|
||||
}
|
||||
return success, nil
|
||||
}
|
||||
|
||||
// Locked returns true if the node is locked.
|
||||
func (l *Lock) Locked() bool {
|
||||
l.mux.RLock()
|
||||
defer l.mux.RUnlock()
|
||||
return l.locked
|
||||
return true, vtpm.MarkNodeAsBootstrapped(l.tpm, ownerID, clusterID)
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ This checklist will prepare `v1.3.0` from `v1.2.0`. Adjust your version numbers
|
||||
* Version of the image to build: `1.3.0`
|
||||
3. Create a new branch to prepare the following things:
|
||||
1. Review and update changelog with all changes since last release. [GitHub's diff view](https://github.com/edgelesssys/constellation/compare/v1.2.0...main) helps a lot!
|
||||
2. Update versions [images.go](../coordinator/kubernetes/k8sapi/resources/images.go) to `v1.3.0`
|
||||
2. Update versions [images.go](../bootstrapper/kubernetes/k8sapi/resources/images.go) to `v1.3.0`
|
||||
3. Merge this branch
|
||||
4. Create a new tag in `constellation`
|
||||
* `git tag v1.3.0`
|
||||
|
2
go.sum
2
go.sum
@ -1018,7 +1018,6 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9
|
||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||
github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
|
||||
github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY=
|
||||
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
|
||||
github.com/lpabon/godbc v0.1.1/go.mod h1:Jo9QV0cf3U6jZABgiJ2skINAXb9j8m51r07g4KI92ZA=
|
||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||
@ -2324,7 +2323,6 @@ k8s.io/kube-controller-manager v0.24.0/go.mod h1:s0pbwI8UuBEDdXQbTUpQdNIyU4rQ7jO
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||
k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 h1:Gii5eqf+GmIEwGNKQYQClCayuJCe2/4fZUvF7VG99sU=
|
||||
k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk=
|
||||
k8s.io/kube-proxy v0.24.0 h1:p8KNQT+OrCU1T2xQAdmVl/+2YJOHyUD6IZc+h+Jjjho=
|
||||
k8s.io/kube-proxy v0.24.0/go.mod h1:OZ1k9jSwW94Rmj5hepCFea7qlGvvU+bfcosc6+dcFKA=
|
||||
k8s.io/kube-scheduler v0.24.0/go.mod h1:DUq+fXaC51N1kl2YnT2EZSxOph6JOmIJe/pQe5keZPc=
|
||||
k8s.io/kubectl v0.24.0/go.mod h1:pdXkmCyHiRTqjYfyUJiXtbVNURhv0/Q1TyRhy2d5ic0=
|
||||
|
17
hack/go.mod
17
hack/go.mod
@ -50,6 +50,21 @@ require (
|
||||
libvirt.org/go/libvirt v1.8004.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.3.1 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/sergi/go-diff v1.2.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.100.2 // indirect
|
||||
cloud.google.com/go/compute v1.5.0 // indirect
|
||||
@ -103,6 +118,7 @@ require (
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||
github.com/go-git/go-git/v5 v5.4.2
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.11.0 // indirect
|
||||
@ -134,6 +150,7 @@ require (
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
|
||||
golang.org/x/mod v0.5.1
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a // indirect
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
|
||||
|
47
hack/go.sum
47
hack/go.sum
@ -161,11 +161,18 @@ github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0
|
||||
github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
|
||||
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE=
|
||||
@ -174,6 +181,7 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
@ -190,6 +198,7 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||
@ -334,6 +343,7 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
@ -368,7 +378,17 @@ github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49P
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||
github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
|
||||
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
|
||||
github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
|
||||
github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@ -619,12 +639,16 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:
|
||||
github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||
github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4=
|
||||
github.com/jhump/protoreflect v1.8.2/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg=
|
||||
github.com/jhump/protoreflect v1.9.0/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg=
|
||||
@ -653,6 +677,8 @@ github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSg
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
@ -684,6 +710,8 @@ github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0U
|
||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
|
||||
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
@ -740,6 +768,7 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE
|
||||
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62PewwiQTlm/7Rj+cxVYqZvDIUc+JjZq6GHAC1fsObQ=
|
||||
github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso=
|
||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||
@ -854,9 +883,11 @@ github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
@ -932,6 +963,8 @@ github.com/willdonnelly/passwd v0.0.0-20141013001024-7935dab3074c h1:4+NVyrLUuEm
|
||||
github.com/willdonnelly/passwd v0.0.0-20141013001024-7935dab3074c/go.mod h1:xcvfY9pOw6s4wyrhilFSbMthL6KzgrfCIETHHUOQ/fQ=
|
||||
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
|
||||
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
@ -1033,6 +1066,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
@ -1076,6 +1110,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -1127,6 +1163,7 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
@ -1197,6 +1234,7 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -1238,11 +1276,13 @@ golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210316092937-0b90fd5c4c48/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210412220455-f1c623a9e750/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -1268,6 +1308,7 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -1578,6 +1619,7 @@ gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUy
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
@ -1595,6 +1637,7 @@ gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzE
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@ -1619,11 +1662,11 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
libvirt.org/go/libvirt v1.8004.0 h1:SKa5hQNKQfc1VjU4LqLMorqPCxC1lplnz8LwLiMrPyM=
|
||||
libvirt.org/go/libvirt v1.8004.0/go.mod h1:1WiFE8EjZfq+FCVog+rvr1yatKbKZ9FaFMZgEqxEJqQ=
|
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc=
|
||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
libvirt.org/go/libvirt v1.8004.0 h1:SKa5hQNKQfc1VjU4LqLMorqPCxC1lplnz8LwLiMrPyM=
|
||||
libvirt.org/go/libvirt v1.8004.0/go.mod h1:1WiFE8EjZfq+FCVog+rvr1yatKbKZ9FaFMZgEqxEJqQ=
|
||||
pack.ag/amqp v0.11.2/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
|
119
hack/pseudo-version/internal/git/git.go
Normal file
119
hack/pseudo-version/internal/git/git.go
Normal file
@ -0,0 +1,119 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
git "github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
"github.com/go-git/go-git/v5/plumbing/storer"
|
||||
)
|
||||
|
||||
var versionRegex = regexp.MustCompile(`^v\d+\.\d+\.\d+$`)
|
||||
|
||||
type Git struct {
|
||||
repo *git.Repository
|
||||
}
|
||||
|
||||
func New() (*Git, error) {
|
||||
repo, err := git.PlainOpenWithOptions("", &git.PlainOpenOptions{DetectDotGit: true})
|
||||
return &Git{repo: repo}, err
|
||||
}
|
||||
|
||||
// Revision returns the current revision (HEAD) of the repository in the format used by go pseudo versions.
|
||||
func (g *Git) Revision() (string, time.Time, error) {
|
||||
commitRef, err := g.repo.Head()
|
||||
if err != nil {
|
||||
return "", time.Time{}, err
|
||||
}
|
||||
commit, err := g.repo.CommitObject(commitRef.Hash())
|
||||
if err != nil {
|
||||
return "", time.Time{}, err
|
||||
}
|
||||
return commitRef.Hash().String()[:8], commit.Author.When, nil
|
||||
}
|
||||
|
||||
// FirstParentWithVersionTag returns the first parent of the HEAD commit (or HEAD itself) that has a version tag.
|
||||
func (g *Git) FirstParentWithVersionTag() (revision string, versionTag string, err error) {
|
||||
commitRef, err := g.repo.Head()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
commit, err := g.repo.CommitObject(commitRef.Hash())
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
commitToHash, err := g.tagsByRevisionHash()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
iter := object.NewCommitIterCTime(commit, nil, nil)
|
||||
if err := iter.ForEach(
|
||||
func(c *object.Commit) error {
|
||||
tags, ok := commitToHash[c.Hash.String()]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
version := g.findVersionTag(tags)
|
||||
if version == nil {
|
||||
return nil
|
||||
}
|
||||
versionTag = *version
|
||||
revision = c.Hash.String()
|
||||
return storer.ErrStop
|
||||
},
|
||||
); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if revision == "" || versionTag == "" {
|
||||
return "", "", errors.New("no version tag found")
|
||||
}
|
||||
return revision, versionTag, nil
|
||||
}
|
||||
|
||||
// tagsByRevisionHash returns a map from revision hash to a list of associated tags.
|
||||
func (g *Git) tagsByRevisionHash() (map[string][]string, error) {
|
||||
tags := make(map[string][]string)
|
||||
refs, err := g.repo.Tags()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := refs.ForEach(
|
||||
func(ref *plumbing.Reference) error {
|
||||
tag, err := g.repo.TagObject(ref.Hash())
|
||||
switch err {
|
||||
case nil:
|
||||
// Tag object present
|
||||
case plumbing.ErrObjectNotFound:
|
||||
// Not a tag object
|
||||
return nil
|
||||
default:
|
||||
// Some other error
|
||||
return err
|
||||
}
|
||||
commit, err := tag.Commit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
commitHash := commit.Hash.String()
|
||||
tags[commitHash] = append(tags[commitHash], tag.Name)
|
||||
return nil
|
||||
},
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
// findVersionTag tries to find a tag for a semantic version (e.g.: v1.0.0).
|
||||
func (g *Git) findVersionTag(tags []string) *string {
|
||||
for _, tag := range tags {
|
||||
if versionRegex.MatchString(tag) {
|
||||
return &tag
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
60
hack/pseudo-version/pseudo-version.go
Normal file
60
hack/pseudo-version/pseudo-version.go
Normal file
@ -0,0 +1,60 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/edgelesssys/constellation/hack/pseudo-version/internal/git"
|
||||
"github.com/edgelesssys/constellation/internal/logger"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"golang.org/x/mod/module"
|
||||
)
|
||||
|
||||
func main() {
|
||||
major := flag.String("major", "v1", "Optional major version")
|
||||
base := flag.String("base", "", "Optional base version")
|
||||
revisionTimestamp := flag.String("time", "", "Optional revision time")
|
||||
revision := flag.String("revision", "", "Optional revision (git commit hash)")
|
||||
flag.Parse()
|
||||
|
||||
log := logger.New(logger.JSONLog, zapcore.InfoLevel)
|
||||
|
||||
gitc, err := git.New()
|
||||
if err != nil {
|
||||
log.With(zap.Error(err)).Fatalf("Failed to initialize git client")
|
||||
}
|
||||
|
||||
if *base == "" {
|
||||
_, versionTag, err := gitc.FirstParentWithVersionTag()
|
||||
if err != nil {
|
||||
log.With(zap.Error(err)).Fatalf("Failed to find base version")
|
||||
}
|
||||
*base = versionTag
|
||||
}
|
||||
|
||||
var headRevision string
|
||||
var headTime time.Time
|
||||
if *revisionTimestamp == "" || *revision == "" {
|
||||
var err error
|
||||
headRevision, headTime, err = gitc.Revision()
|
||||
if err != nil {
|
||||
log.With(zap.Error(err)).Fatalf("Failed to retrieve HEAD")
|
||||
}
|
||||
}
|
||||
|
||||
if *revisionTimestamp != "" {
|
||||
headTime, err := time.Parse("20060102150405", *revisionTimestamp)
|
||||
if err != nil {
|
||||
log.With(zap.Error(err)).With("time", headTime).Fatalf("Failed to parse revision timestamp")
|
||||
}
|
||||
}
|
||||
|
||||
if *revision == "" {
|
||||
*revision = headRevision
|
||||
}
|
||||
|
||||
version := module.PseudoVersion(*major, *base, headTime, *revision)
|
||||
fmt.Println(version)
|
||||
}
|
@ -3,21 +3,10 @@
|
||||
Implementation for Constellation's node flow to join an existing cluster.
|
||||
|
||||
The join service runs on each control-plane node of the Kubernetes cluster.
|
||||
New nodes (at cluster start, or later through autoscaling) send an IssueJoinTicket request to the service over [aTLS](../coordinator/atls/).
|
||||
New nodes (at cluster start, or later through autoscaling) send an IssueJoinTicket request to the service over [aTLS](../bootstrapper/atls/).
|
||||
The join service verifies the new nodes certificate and attestation statement.
|
||||
If attestation is successful, the new node is supplied with a disk encryption key for its state disk, and a Kubernetes bootstrap token, so it may join the cluster.
|
||||
|
||||
The join service uses klog v2 for logging.
|
||||
Use the `-v` flag to set the log verbosity level.
|
||||
Use different verbosity levels during development depending on the information:
|
||||
|
||||
* 2 for information that should always be logged. Examples: server starting, new gRPC request.
|
||||
|
||||
* 4 for general logging. If you are unsure what log level to use, use 4.
|
||||
|
||||
* 6 for low level information logging. Example: values of new expected measurements
|
||||
|
||||
* Potentially sensitive information, such as return values of functions should never be logged.
|
||||
|
||||
## Packages
|
||||
|
||||
@ -36,7 +25,7 @@ sequenceDiagram
|
||||
participant New Node
|
||||
participant Join Service
|
||||
New Node-->>Join Service: aTLS Handshake (server side verification)
|
||||
Join Service-->>New Node:
|
||||
Join Service-->>New Node: #
|
||||
New Node->>+Join Service: grpc::IssueJoinTicket(DiskUUID, NodeName, IsControlPlane)
|
||||
Join Service->>+KMS: grpc::GetDataKey(DiskUUID)
|
||||
KMS->>-Join Service: DiskEncryptionKey
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"net"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
azurecloud "github.com/edgelesssys/constellation/bootstrapper/cloudprovider/azure"
|
||||
gcpcloud "github.com/edgelesssys/constellation/bootstrapper/cloudprovider/gcp"
|
||||
@ -26,17 +27,16 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// vpcIPTimeout is the maximum amount of time to wait for retrieval of the VPC ip.
|
||||
const vpcIPTimeout = 30 * time.Second
|
||||
|
||||
func main() {
|
||||
provider := flag.String("cloud-provider", "", "cloud service provider this binary is running on")
|
||||
kmsEndpoint := flag.String("kms-endpoint", "", "endpoint of Constellations key management service")
|
||||
verbosity := flag.Int("v", 0, logger.CmdLineVerbosityDescription)
|
||||
flag.Parse()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
log := logger.New(logger.JSONLog, logger.VerbosityFromInt(*verbosity))
|
||||
|
||||
log.With(zap.String("version", constants.VersionInfo), zap.String("cloudProvider", *provider)).
|
||||
Infof("Constellation Node Join Service")
|
||||
|
||||
@ -50,6 +50,8 @@ func main() {
|
||||
|
||||
creds := atlscredentials.New(nil, []atls.Validator{validator})
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), vpcIPTimeout)
|
||||
defer cancel()
|
||||
vpcIP, err := getVPCIP(ctx, *provider)
|
||||
if err != nil {
|
||||
log.With(zap.Error(err)).Fatalf("Failed to get IP in VPC")
|
||||
|
@ -67,8 +67,8 @@ func (k *Kubeadm) GetJoinToken(ttl time.Duration) (*kubeadm.BootstrapTokenDiscov
|
||||
Token: tokenStr,
|
||||
Description: "Bootstrap token generated by Constellation's Join service",
|
||||
TTL: &metav1.Duration{Duration: ttl},
|
||||
Usages: []string{"signing", "authentication"},
|
||||
Groups: []string{"system:bootstrappers:kubeadm:default-node-token"},
|
||||
Usages: kubeconstants.DefaultTokenUsages,
|
||||
Groups: kubeconstants.DefaultTokenGroups,
|
||||
}
|
||||
|
||||
// create the token in Kubernetes
|
||||
@ -113,33 +113,23 @@ func (k *Kubeadm) GetControlPlaneCertificatesAndKeys() (map[string][]byte, error
|
||||
k.log.Infof("Loading control plane certificates and keys")
|
||||
controlPlaneFiles := make(map[string][]byte)
|
||||
|
||||
keyFilenames := []string{
|
||||
filenames := []string{
|
||||
kubeconstants.CAKeyName,
|
||||
kubeconstants.ServiceAccountPrivateKeyName,
|
||||
kubeconstants.FrontProxyCAKeyName,
|
||||
kubeconstants.EtcdCAKeyName,
|
||||
}
|
||||
certFilenames := []string{
|
||||
kubeconstants.CACertName,
|
||||
kubeconstants.ServiceAccountPublicKeyName,
|
||||
kubeconstants.FrontProxyCACertName,
|
||||
kubeconstants.EtcdCACertName,
|
||||
}
|
||||
|
||||
for _, keyFilename := range keyFilenames {
|
||||
key, err := k.file.Read(filepath.Join(kubeconstants.KubernetesDir, kubeconstants.DefaultCertificateDir, keyFilename))
|
||||
for _, filename := range filenames {
|
||||
key, err := k.file.Read(filepath.Join(kubeconstants.KubernetesDir, kubeconstants.DefaultCertificateDir, filename))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
controlPlaneFiles[keyFilename] = key
|
||||
}
|
||||
|
||||
for _, certFilename := range certFilenames {
|
||||
cert, err := k.file.Read(filepath.Join(kubeconstants.KubernetesDir, kubeconstants.DefaultCertificateDir, certFilename))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
controlPlaneFiles[certFilename] = cert
|
||||
controlPlaneFiles[filename] = key
|
||||
}
|
||||
|
||||
return controlPlaneFiles, nil
|
||||
|
@ -70,14 +70,6 @@ func (s *Server) Run(creds credentials.TransportCredentials, port string) error
|
||||
// - a decryption key for CA certificates uploaded to the Kubernetes cluster.
|
||||
func (s *Server) IssueJoinTicket(ctx context.Context, req *joinproto.IssueJoinTicketRequest) (resp *joinproto.IssueJoinTicketResponse, retErr error) {
|
||||
s.log.Infof("IssueJoinTicket called")
|
||||
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
s.log.Errorf("IssueJoinTicket failed: %s", retErr)
|
||||
retErr = fmt.Errorf("IssueJoinTicket failed: %w", retErr)
|
||||
}
|
||||
}()
|
||||
|
||||
log := s.log.With(zap.String("peerAddress", grpclog.PeerAddrFromContext(ctx)))
|
||||
log.Infof("Loading IDs")
|
||||
var id attestationtypes.ID
|
||||
@ -108,10 +100,11 @@ func (s *Server) IssueJoinTicket(ctx context.Context, req *joinproto.IssueJoinTi
|
||||
|
||||
var controlPlaneFiles []*joinproto.ControlPlaneCertOrKey
|
||||
if req.IsControlPlane {
|
||||
log.Infof("Creating control plane certificate key")
|
||||
log.Infof("Loading control plane certificates and keys")
|
||||
filesMap, err := s.joinTokenGetter.GetControlPlaneCertificatesAndKeys()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ActivateControlPlane failed: %w", err)
|
||||
log.With(zap.Error(err)).Errorf("Failed to load control plane certificates and keys")
|
||||
return nil, status.Errorf(codes.Internal, "ActivateControlPlane failed: %s", err)
|
||||
}
|
||||
|
||||
for k, v := range filesMap {
|
||||
|
@ -12,7 +12,7 @@ type ClusterKMS struct {
|
||||
masterKey []byte
|
||||
}
|
||||
|
||||
// CreateKEK sets the CoordinatorKMS masterKey.
|
||||
// CreateKEK sets the ClusterKMS masterKey.
|
||||
func (c *ClusterKMS) CreateKEK(ctx context.Context, keyID string, kek []byte) error {
|
||||
c.masterKey = kek
|
||||
return nil
|
||||
|
@ -12,7 +12,7 @@ func TestMain(m *testing.M) {
|
||||
goleak.VerifyTestMain(m)
|
||||
}
|
||||
|
||||
func TestCoordinatorKMS(t *testing.T) {
|
||||
func TestClusterKMS(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
kms := &ClusterKMS{}
|
||||
masterKey := []byte("Constellation")
|
||||
|
@ -16,9 +16,9 @@ type ConstellationKMS struct {
|
||||
}
|
||||
|
||||
// NewConstellationKMS initializes a ConstellationKMS.
|
||||
func NewConstellationKMS(coordinatorEndpoint string) *ConstellationKMS {
|
||||
func NewConstellationKMS(endpoint string) *ConstellationKMS {
|
||||
return &ConstellationKMS{
|
||||
endpoint: coordinatorEndpoint, // default: "kms.kube-system:9000"
|
||||
endpoint: endpoint, // default: "kms.kube-system:9000"
|
||||
kms: &constellationKMSClient{},
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,9 @@ import (
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// KeyAPI is the interface called by the Coordinator or an admin during restart of a node.
|
||||
// KeyAPI is the interface called by control-plane or an admin during restart of a node.
|
||||
type KeyAPI struct {
|
||||
listenAddr string
|
||||
log *logger.Logger
|
||||
mux sync.Mutex
|
||||
metadata metadata.InstanceLister
|
||||
@ -60,12 +61,12 @@ func (a *KeyAPI) PushStateDiskKey(ctx context.Context, in *keyproto.PushStateDis
|
||||
return &keyproto.PushStateDiskKeyResponse{}, nil
|
||||
}
|
||||
|
||||
// WaitForDecryptionKey notifies the Coordinator to send a decryption key and waits until a key is received.
|
||||
// WaitForDecryptionKey notifies control-plane nodes to send a decryption key and waits until a key is received.
|
||||
func (a *KeyAPI) WaitForDecryptionKey(uuid, listenAddr string) ([]byte, error) {
|
||||
if uuid == "" {
|
||||
return nil, errors.New("received no disk UUID")
|
||||
}
|
||||
|
||||
a.listenAddr = listenAddr
|
||||
creds := atlscredentials.New(a.issuer, nil)
|
||||
server := grpc.NewServer(grpc.Creds(creds))
|
||||
keyproto.RegisterAPIServer(server, a)
|
||||
@ -79,10 +80,7 @@ func (a *KeyAPI) WaitForDecryptionKey(uuid, listenAddr string) ([]byte, error) {
|
||||
go server.Serve(listener)
|
||||
defer server.GracefulStop()
|
||||
|
||||
if err := a.requestKeyLoop(uuid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a.requestKeyLoop(uuid)
|
||||
return a.key, nil
|
||||
}
|
||||
|
||||
@ -91,11 +89,11 @@ func (a *KeyAPI) ResetKey() {
|
||||
a.key = nil
|
||||
}
|
||||
|
||||
// requestKeyLoop continuously requests decryption keys from all available Coordinators, until the KeyAPI receives a key.
|
||||
func (a *KeyAPI) requestKeyLoop(uuid string, opts ...grpc.DialOption) error {
|
||||
// we do not perform attestation, since the restarting node does not need to care about notifying the correct Coordinator
|
||||
// requestKeyLoop continuously requests decryption keys from all available control-plane nodes, until the KeyAPI receives a key.
|
||||
func (a *KeyAPI) requestKeyLoop(uuid string, opts ...grpc.DialOption) {
|
||||
// we do not perform attestation, since the restarting node does not need to care about notifying the correct node
|
||||
// if an incorrect key is pushed by a malicious actor, decrypting the disk will fail, and the node will not start
|
||||
creds := atlscredentials.New(nil, nil)
|
||||
creds := atlscredentials.New(a.issuer, nil)
|
||||
// set up for the select statement to immediately request a key, skipping the initial delay caused by using a ticker
|
||||
firstReq := make(chan struct{}, 1)
|
||||
firstReq <- struct{}{}
|
||||
@ -106,10 +104,10 @@ func (a *KeyAPI) requestKeyLoop(uuid string, opts ...grpc.DialOption) error {
|
||||
select {
|
||||
// return if a key was received
|
||||
// a key can be send by
|
||||
// - a Coordinator, after the request rpc was received
|
||||
// - a control-plane node, after the request rpc was received
|
||||
// - by a Constellation admin, at any time this loop is running on a node during boot
|
||||
case <-a.keyReceived:
|
||||
return nil
|
||||
return
|
||||
case <-ticker.C:
|
||||
a.requestKey(uuid, creds, opts...)
|
||||
case <-firstReq:
|
||||
@ -119,22 +117,36 @@ func (a *KeyAPI) requestKeyLoop(uuid string, opts ...grpc.DialOption) error {
|
||||
}
|
||||
|
||||
func (a *KeyAPI) requestKey(uuid string, credentials credentials.TransportCredentials, opts ...grpc.DialOption) {
|
||||
// list available Coordinators
|
||||
// list available control-plane nodes
|
||||
endpoints, _ := metadata.KMSEndpoints(context.Background(), a.metadata)
|
||||
|
||||
a.log.With(zap.Strings("endpoints", endpoints)).Infof("Sending a key request to available Coordinators")
|
||||
// notify all available Coordinators to send a key to the node
|
||||
a.log.With(zap.Strings("endpoints", endpoints)).Infof("Sending a key request to available control-plane nodes")
|
||||
// notify all available control-plane nodes to send a key to the node
|
||||
// any errors encountered here will be ignored, and the calls retried after a timeout
|
||||
for _, endpoint := range endpoints {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), a.timeout)
|
||||
defer cancel()
|
||||
conn, err := grpc.DialContext(ctx, endpoint, append(opts, grpc.WithTransportCredentials(credentials))...)
|
||||
if err == nil {
|
||||
client := kmsproto.NewAPIClient(conn)
|
||||
_, _ = client.GetDataKey(ctx, &kmsproto.GetDataKeyRequest{DataKeyId: uuid, Length: constants.StateDiskKeyLength})
|
||||
conn.Close()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
defer conn.Close()
|
||||
client := kmsproto.NewAPIClient(conn)
|
||||
response, err := client.GetDataKey(ctx, &kmsproto.GetDataKeyRequest{DataKeyId: uuid, Length: constants.StateDiskKeyLength})
|
||||
if err != nil {
|
||||
a.log.With(zap.Error(err), zap.String("endpoint", endpoint)).Warnf("Failed to request key")
|
||||
continue
|
||||
}
|
||||
pushKeyConn, err := grpc.DialContext(ctx, a.listenAddr, append(opts, grpc.WithTransportCredentials(credentials))...)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
defer pushKeyConn.Close()
|
||||
pushKeyClient := keyproto.NewAPIClient(pushKeyConn)
|
||||
if _, err := pushKeyClient.PushStateDiskKey(ctx, &keyproto.PushStateDiskKeyRequest{StateDiskKey: response.DataKey}); err != nil {
|
||||
a.log.With(zap.Error(err), zap.String("endpoint", a.listenAddr)).Errorf("Failed to push key")
|
||||
continue
|
||||
}
|
||||
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,6 @@ func TestRequestKeyLoop(t *testing.T) {
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
keyReceived := make(chan struct{}, 1)
|
||||
@ -106,13 +105,12 @@ func TestRequestKeyLoop(t *testing.T) {
|
||||
keyReceived <- struct{}{}
|
||||
}()
|
||||
|
||||
err := keyWaiter.requestKeyLoop(
|
||||
keyWaiter.requestKeyLoop(
|
||||
"1234",
|
||||
grpc.WithContextDialer(func(ctx context.Context, s string) (net.Conn, error) {
|
||||
return listener.DialContext(ctx)
|
||||
}),
|
||||
)
|
||||
assert.NoError(err)
|
||||
|
||||
s.Stop()
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user