Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing bug with standalone mode #6

Merged
4 commits merged into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
*~
tofu
tofu.exe
fake
fake.exe
22 changes: 15 additions & 7 deletions downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,20 @@ func New(opts ...ConfigOpt) (Downloader, error) {
}
}

key, err := crypto.NewKeyFromArmored(cfg.GPGKey)
keyRing, err := createKeyRing(cfg.GPGKey)
if err != nil {
return nil, err
}

return &downloader{
cfg,
tpl,
keyRing,
}, nil
}

func createKeyRing(armoredKey string) (*crypto.KeyRing, error) {
key, err := crypto.NewKeyFromArmored(armoredKey)
if err != nil {
return nil, &InvalidConfigurationError{
Message: "Failed to decode GPG key",
Expand All @@ -61,12 +74,7 @@ func New(opts ...ConfigOpt) (Downloader, error) {
if err != nil {
return nil, &InvalidConfigurationError{Message: "Cannot create keyring", Cause: err}
}

return &downloader{
cfg,
tpl,
keyRing,
}, nil
return keyRing, nil
}

type downloader struct {
Expand Down
26 changes: 9 additions & 17 deletions mockmirror/fake.go → internal/helloworld/fake.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0

package mockmirror
package helloworld

import (
"errors"
Expand All @@ -12,18 +12,14 @@ import (
"testing"
)

func buildFake(t *testing.T) []byte {
_, filename, _, _ := runtime.Caller(1)
fakeDir := path.Join(path.Dir(filename), "fake")
if err := os.MkdirAll(fakeDir, 0755); err != nil {
t.Fatalf("Failed to create fake dir (%v)", err)
}
binaryPath := path.Join(fakeDir, "fake")
if contents, err := os.ReadFile(binaryPath); err == nil {
return contents
// Build creates a hello-world binary for the current platform you can use to test.
func Build(t *testing.T) []byte {
fakeName := "fake"
if runtime.GOOS == "windows" {
fakeName += ".exe"
}

dir := path.Join(os.TempDir(), "fake")
dir := path.Join(os.TempDir(), fakeName)
if err := os.MkdirAll(dir, 0700); err != nil {
t.Fatal(err)
}
Expand All @@ -37,7 +33,7 @@ func buildFake(t *testing.T) []byte {
t.Fatal()
}

cmd := exec.Command("go", "build", "-ldflags", "-s -w", "-o", "fake")
cmd := exec.Command("go", "build", "-ldflags", "-s -w", "-o", fakeName)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = dir
Expand All @@ -50,14 +46,10 @@ func buildFake(t *testing.T) []byte {
}
}

contents, err := os.ReadFile(path.Join(dir, "fake"))
contents, err := os.ReadFile(path.Join(dir, fakeName))
if err != nil {
t.Fatalf("Failed to read compiled fake (%v)", err)
}

if err := os.WriteFile(binaryPath, contents, 0700); err != nil { //nolint:gosec //This needs to be executable.
t.Fatalf("Failed to create fake binary at %s (%v)", binaryPath, err)
}
return contents
}

Expand Down
124 changes: 0 additions & 124 deletions internal/tools/mockmirror-build-fake/main.go

This file was deleted.

17 changes: 17 additions & 0 deletions mirror.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import (
"fmt"
"net/http"
"time"

"github.com/ProtonMail/gopenpgp/v2/crypto"
"github.com/opentofu/tofudl/branding"
)

// NewMirror creates a new mirror, optionally acting as a pull-through cache when passing a pullThroughDownloader.
Expand All @@ -17,10 +20,20 @@ func NewMirror(config MirrorConfig, storage MirrorStorage, pullThroughDownloader
"no storage and no pull-through downloader passed to NewMirror, cannot create a working mirror",
)
}
if config.GPGKey == "" {
config.GPGKey = branding.DefaultGPGKey
}

keyRing, err := createKeyRing(config.GPGKey)
if err != nil {
return nil, err
}

return &mirror{
storage,
pullThroughDownloader,
config,
keyRing,
}, nil
}

Expand Down Expand Up @@ -57,10 +70,14 @@ type MirrorConfig struct {
// ArtifactCacheTimeout is the time the cached artifacts should be considered valid. A duration of 0 means that
// artifacts should not be cached. A duration of -1 means that artifacts should be cached indefinitely.
ArtifactCacheTimeout time.Duration `json:"artifact_cache_timeout"`

// GPGKey is the ASCII-armored key to verify downloaded artifacts against. This is only needed in standalone mode.
GPGKey string `json:"gpg_key"`
}

type mirror struct {
storage MirrorStorage
pullThroughDownloader Downloader
config MirrorConfig
keyRing *crypto.KeyRing
}
2 changes: 1 addition & 1 deletion mirror_download_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ import (
)

func (m *mirror) DownloadVersion(ctx context.Context, version VersionWithArtifacts, platform Platform, architecture Architecture) ([]byte, error) {
return downloadVersion(ctx, version, platform, architecture, m.DownloadArtifact, m.pullThroughDownloader.VerifyArtifact)
return downloadVersion(ctx, version, platform, architecture, m.DownloadArtifact, m.VerifyArtifact)
}
51 changes: 51 additions & 0 deletions mirror_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ package tofudl_test

import (
"context"
"runtime"
"testing"
"time"

"github.com/ProtonMail/gopenpgp/v2/crypto"
"github.com/opentofu/tofudl"
"github.com/opentofu/tofudl/branding"
"github.com/opentofu/tofudl/internal/helloworld"
"github.com/opentofu/tofudl/mockmirror"
)

Expand Down Expand Up @@ -92,3 +96,50 @@ func TestMirroringE2E(t *testing.T) {
t.Fatal("Empty artifact!")
}
}

func TestMirrorStandalone(t *testing.T) {
binaryContents := helloworld.Build(t)

ctx := context.Background()
key, err := crypto.GenerateKey(branding.ProductName+" Test", "[email protected]", "rsa", 2048)
if err != nil {
t.Fatal(err)
}
pubKey, err := key.GetArmoredPublicKey()
if err != nil {
t.Fatal(err)
}
builder, err := tofudl.NewReleaseBuilder(key)
if err != nil {
t.Fatal(err)
}
if err := builder.PackageBinary(tofudl.PlatformAuto, tofudl.ArchitectureAuto, binaryContents, nil); err != nil {
t.Fatalf("failed to package binary (%v)", err)
}

mirrorStorage, err := tofudl.NewFilesystemStorage(t.TempDir())
if err != nil {
t.Fatalf("failed to set up TofuDL mirror")
}
downloader, err := tofudl.NewMirror(
tofudl.MirrorConfig{
GPGKey: pubKey,
},
mirrorStorage,
nil,
)
if err != nil {
t.Fatal(err)
}
if err := builder.Build(ctx, "1.9.0", downloader); err != nil {
t.Fatal(err)
}
_, err = downloader.Download(ctx)
if err != nil {
t.Fatal(err)
}
// Make sure all file handles are closed.
if runtime.GOOS == "windows" {
runtime.GC()
}
}
5 changes: 4 additions & 1 deletion mirror_verify_artifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
package tofudl

func (m *mirror) VerifyArtifact(artifactName string, artifactContents []byte, sumsFileContents []byte, signatureFileContent []byte) error {
return m.pullThroughDownloader.VerifyArtifact(artifactName, artifactContents, sumsFileContents, signatureFileContent)
if m.pullThroughDownloader != nil {
return m.pullThroughDownloader.VerifyArtifact(artifactName, artifactContents, sumsFileContents, signatureFileContent)
}
return verifyArtifact(m.keyRing, artifactName, artifactContents, sumsFileContents, signatureFileContent)
}
3 changes: 2 additions & 1 deletion mockmirror/mockmirror.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ import (
"github.com/ProtonMail/gopenpgp/v2/crypto"
"github.com/opentofu/tofudl"
"github.com/opentofu/tofudl/branding"
"github.com/opentofu/tofudl/internal/helloworld"
)

// New returns a mirror serving a fake archive signed with a GPG key for testing purposes.
func New(
t *testing.T,
) Mirror {
return NewFromBinary(t, buildFake(t))
return NewFromBinary(t, helloworld.Build(t))
}

// NewFromBinary returns a mirror serving a binary passed and signed with a GPG key for testing purposes.
Expand Down