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

Allow downstream projects to import func-e #446

Merged
merged 2 commits into from
Aug 22, 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
111 changes: 111 additions & 0 deletions api/run.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright 2022 Tetrate
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package api allows go projects to use func-e as a library.
package api

import (
"context"
"io"
"os"
"runtime"

"github.com/tetratelabs/func-e/internal/cmd"
"github.com/tetratelabs/func-e/internal/globals"
"github.com/tetratelabs/func-e/internal/version"
)

// HomeDir is an absolute path which most importantly contains "versions"
// installed from EnvoyVersionsURL. Defaults to "${HOME}/.func-e"
func HomeDir(homeDir string) RunOption {
return func(o *runOpts) {
o.homeDir = homeDir
}
}

// EnvoyVersionsURL is the path to the envoy-versions.json.
// Defaults to "https://archive.tetratelabs.io/envoy/envoy-versions.json"
func EnvoyVersionsURL(envoyVersionsURL string) RunOption {
return func(o *runOpts) {
o.envoyVersionsURL = envoyVersionsURL
}
}

// EnvoyVersion overrides the version of Envoy to run. Defaults to the
// contents of "$HomeDir/versions/version".
//
// When that file is missing, it is generated from ".latestVersion" from the
// EnvoyVersionsURL. Its value can be in full version major.minor.patch format,
// e.g. 1.18.1 or without patch component, major.minor, e.g. 1.18.
func EnvoyVersion(envoyVersion string) RunOption {
return func(o *runOpts) {
o.envoyVersion = envoyVersion
}
}

// Out is where status messages are written. Defaults to os.Stdout
func Out(out io.Writer) RunOption {
return func(o *runOpts) {
o.out = out
}
}

// RunOption is configuration for Run.
type RunOption func(*runOpts)

type runOpts struct {
homeDir string
envoyVersion string
envoyVersionsURL string
out io.Writer
}

// Run downloads Envoy and runs it as a process with the arguments
// passed to it. Use RunOption for configuration options.
func Run(ctx context.Context, args []string, options ...RunOption) error {
ro := &runOpts{
homeDir: globals.DefaultHomeDir,
envoyVersion: "", // default to lookup
envoyVersionsURL: globals.DefaultEnvoyVersionsURL,
out: os.Stdout,
}
for _, option := range options {
option(ro)
}

o := globals.GlobalOpts{
HomeDir: ro.homeDir,
EnvoyVersion: version.PatchVersion(ro.envoyVersion),
EnvoyVersionsURL: ro.envoyVersion,
Out: ro.out,
}

funcECmd := cmd.NewApp(&o)

funcERunArgs := []string{"func-e", "--platform", runtime.GOOS + "/" + runtime.GOARCH, "run"}
funcERunArgs = append(funcERunArgs, args...)

errChan := make(chan error)
go func() {
errChan <- funcECmd.RunContext(ctx, funcERunArgs)
}()

// Wait for run to exit or an explicit stop.
select {
case <-ctx.Done():
return nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we kill the envoy process in this case? (not sure if there's such function in func-e now)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this done in the called func libs

// At this point, shutdown hooks have run and Envoy is interrupted.
?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok cool but it seems to me that the code block only reaches when the OS signal was sent. Is there any way to verify that it works as intended? e.g. passing the fake config that runs indefinitely in tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you point me to some tests in the code base that verify this, that I can peruse here ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry no idea. Could you do that by yourself ?

case err := <-errChan:
return err
mathetake marked this conversation as resolved.
Show resolved Hide resolved
}
}
81 changes: 81 additions & 0 deletions api/run_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright 2022 Tetrate
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package api

import (
"bytes"
"context"
"os"
"path/filepath"
"testing"
"time"

"github.com/stretchr/testify/require"

"github.com/tetratelabs/func-e/internal/test"
"github.com/tetratelabs/func-e/internal/version"
)

var (
runArgs = []string{"--version"}
)

func TestRunWithCtxDone(t *testing.T) {

tmpDir := t.TempDir()
envoyVersion := version.LastKnownEnvoy
versionsServer := test.RequireEnvoyVersionsTestServer(t, envoyVersion)
defer versionsServer.Close()
envoyVersionsURL := versionsServer.URL + "/envoy-versions.json"
b := bytes.NewBufferString("")

require.Equal(t, 0, b.Len())

ctx := context.Background()
// Use a very small ctx timeout
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
defer cancel()
err := Run(ctx, runArgs, Out(b), HomeDir(tmpDir), EnvoyVersionsURL(envoyVersionsURL))
require.NoError(t, err)

require.NotEqual(t, 0, b.Len())
_, err = os.Stat(filepath.Join(tmpDir, "versions"))
require.NoError(t, err)
}

func TestRunToCompletion(t *testing.T) {

tmpDir := t.TempDir()
envoyVersion := version.LastKnownEnvoy
versionsServer := test.RequireEnvoyVersionsTestServer(t, envoyVersion)
defer versionsServer.Close()
envoyVersionsURL := versionsServer.URL + "/envoy-versions.json"
b := bytes.NewBufferString("")

require.Equal(t, 0, b.Len())

ctx := context.Background()
// Set a large ctx timeout value
ctx, cancel := context.WithTimeout(ctx, 1000*time.Minute)
defer cancel()

err := Run(ctx, runArgs, Out(b), HomeDir(tmpDir), EnvoyVersionsURL(envoyVersionsURL))
require.NoError(t, err)

require.NotEqual(t, 0, b.Len())
_, err = os.Stat(filepath.Join(tmpDir, "versions"))
require.NoError(t, err)

}