Skip to content
Draft
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
64 changes: 64 additions & 0 deletions .github/workflows/functions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,69 @@ jobs:
path: ./cluster_log.txt
retention-days: 7

# ----------------
# E2E EXPOSE TESTS
# ----------------
# External exposure (--expose). The cluster here is KinD, which has no
# route.openshift.io API, so the tests that assert a Route skip themselves via
# IsOpenShift(), and the ones that assert func REFUSES a Route are the ones
# that run. Pointing the same target at an OpenShift cluster runs the other
# half; CI has no OpenShift cluster, so that half is run manually.
test-e2e-expose:
name: E2E - Expose
needs: precheck
runs-on: ubuntu-latest
timeout-minutes: 60
env:
FUNC_CLUSTER_RETRIES: 5
FUNC_E2E_CLEAN: false # cluster only used once
FUNC_E2E_VERBOSE: true
# The keda cases needing keda on the cluster are all Route cases, and
# those skip here. The one keda test that does run refuses a too-long
# name before reaching the cluster at all. Flip to "true" if a keda test
# is added that needs the operator; see PR #3914 ('Insufficient cpu') for
# why it is off by default.
FUNC_CLUSTER_KEDA: "false"
steps:
- uses: actions/checkout@v4
- uses: knative/actions/setup-go@main
- uses: endersonmenezes/free-disk-space@v3
with:
remove_android: true
remove_dotnet: true
remove_haskell: true
remove_swap: true
rm_cmd: "rmz" # Faster than rm

- name: Install Binaries
run: ./hack/binaries.sh
- name: Allocate Cluster
run: ./hack/cluster.sh
- name: Start Local Registry
run: ./hack/registry.sh
- name: Prepare Images
run: ./hack/images.sh

- name: Run Expose E2E Tests
run: make test-e2e-expose

- uses: ./.github/actions/codecov
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: e2e

# Preserve Cluster Logs
- name: Dump Cluster Logs
if: always()
run: ./hack/dump-logs.sh cluster_log.txt
- name: Archive Cluster Logs
if: always()
uses: actions/upload-artifact@v4
with:
name: cluster-logs-e2e-expose
path: ./cluster_log.txt
retention-days: 7

# ----------------
# E2E PODMAN TESTS
# ----------------
Expand Down Expand Up @@ -457,6 +520,7 @@ jobs:
- test-integration
- test-templates
- test-e2e
- test-e2e-expose
- test-e2e-podman
- test-e2e-runtimes
- test-e2e-config-ci
Expand Down
32 changes: 28 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -88,20 +88,36 @@ test: generate/zz_filesystem_generated.go ## Run core unit tests
go test -race -cover -coverprofile=coverage.txt ./...

.PHONY: check
check: check-lint check-goimports check-misspell check-whitespace check-eof ## Check code quality (comprehensive)
check: check-lint check-build-tags check-goimports check-misspell check-whitespace check-eof ## Check code quality (comprehensive)

.PHONY: check-lint
check-lint: $(BIN_GOLANGCI_LINT) ## Run golangci-lint
$(BIN_GOLANGCI_LINT) run --timeout 300s

# cmd/func-util/main.go is behind "exclude_graphdriver_btrfs || !cgo", so the
# default build context compiles only socat.go and every other check in this
# file silently skips it: go vet, golangci-lint and go test all read the
# default context. The sole compile of it anywhere is publish-utils-image in
# functions.yaml, which needs: build and so runs only on a push to main. A type
# error there therefore passes every pull-request check and first fails after
# merge. Vet with the tag so the file is type-checked where the error is cheap.
.PHONY: check-build-tags
check-build-tags: ## Type-check sources the default build context excludes
@echo "Type-checking build-tagged sources..."
go vet -tags exclude_graphdriver_btrfs ./cmd/func-util/

.PHONY: check-goimports
check-goimports: $(BIN_GOIMPORTS) ## Check Go import formatting
@echo "Checking Go import formatting..."
@$(LS_SOURCES) | \
@offenders=$$($(LS_SOURCES) | \
grep '\.go$$' | \
while IFS= read -r file; do [ -f "$$file" ] && echo "$$file"; done | \
xargs $(BIN_GOIMPORTS) -l | grep . && \
(echo "Error: Files with incorrect import formatting found. Run 'goimports -w <file>' to fix."; exit 1) || true
xargs $(BIN_GOIMPORTS) -l); \
if [ -n "$$offenders" ]; then \
echo "$$offenders"; \
echo "Error: Files with incorrect import formatting found. Run 'goimports -w <file>' to fix."; \
exit 1; \
fi

.PHONY: check-misspell
check-misspell: $(BIN_MISSPELL) ## Check for common misspellings
Expand Down Expand Up @@ -317,6 +333,14 @@ test-e2e-lifecycle: func-instrumented-bin ## Run lifecycle hook E2E tests (Start
go test -tags e2e -timeout 60m ./e2e -v -run TestLifecycle_
go tool covdata textfmt -i=$${FUNC_E2E_GOCOVERDIR:-.coverage} -o coverage.txt

.PHONY: test-e2e-expose
test-e2e-expose: func-instrumented-bin ## Run external exposure E2E tests (--expose)
# Runtime and other options can be configured using the FUNC_E2E_* environment variables. see e2e_test.go
# Assertions about a Route skip unless the target cluster is OpenShift, and the
# assertions that func REFUSES a Route skip unless it is not. See records/test-plan-exposure.md
go test -tags e2e -timeout 30m ./e2e -v -run TestExpose_
go tool covdata textfmt -i=$${FUNC_E2E_GOCOVERDIR:-.coverage} -o coverage.txt

.PHONY: test-e2e-config-ci
test-e2e-config-ci: func-instrumented-bin ## CI tests for generated GitHub Workflows
# Runtime and other options can be configured using the FUNC_E2E_* environment variables. see e2e_test.go
Expand Down
29 changes: 20 additions & 9 deletions cmd/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (
"os"

"github.com/ory/viper"
"knative.dev/func/pkg/deployers"
"knative.dev/func/pkg/keda"
"knative.dev/func/pkg/ocproute"

"knative.dev/func/cmd/prompt"
"knative.dev/func/pkg/buildpacks"
Expand Down Expand Up @@ -70,7 +72,8 @@ func NewClient(cfg ClientConfig, options ...fn.Option) (*fn.Client, func()) {
fn.WithRepositoriesPath(config.RepositoriesPath()),
fn.WithScaffolder(buildpacks.NewScaffolder(cfg.Verbose)),
fn.WithBuilder(buildpacks.NewBuilder(buildpacks.WithVerbose(cfg.Verbose))),
fn.WithRemovers(knative.NewRemover(cfg.Verbose), k8s.NewRemover(cfg.Verbose), keda.NewRemover(cfg.Verbose)),
fn.WithRemovers(knative.NewRemover(cfg.Verbose), k8s.NewRemover(cfg.Verbose),
keda.NewRemover(cfg.Verbose, keda.WithRemoverExposer(ocproute.New(deployers.Keda)))),
fn.WithDescribers(
knative.NewDescriber(cfg.Verbose, knative.WithDescriberTransport(t)),
k8s.NewDescriber(cfg.Verbose, k8s.WithDescriberTransport(t)),
Expand Down Expand Up @@ -172,22 +175,30 @@ func newKnativeDeployer(verbose bool) fn.Deployer {
return knative.NewDeployer(options...)
}

// newK8sDeployer builds the raw deployer.
//
// The Exposer is attached unconditionally, not only when the deploy asks for a
// Route. The record saying whether teardown is owed lives on the cluster, so
// wiring time cannot know.
func newK8sDeployer(verbose bool) fn.Deployer {
options := []k8s.DeployerOpt{
return k8s.NewDeployer(
k8s.WithDeployerVerbose(verbose),
k8s.WithDeployerDecorator(deployDecorator{}),
}

return k8s.NewDeployer(options...)
k8s.WithExposer(ocproute.New(deployers.Kubernetes)),
)
}

// newKedaDeployer builds the keda deployer. The Exposer is keda's own, never
// the embedded raw deployer's, so Routes point at the interceptor rather than
// bypassing it. Attached unconditionally for the reason in newK8sDeployer,
// which bites harder here: keda's Route has no owner reference, so a Route
// nothing goes looking for is a Route nothing ever removes.
func newKedaDeployer(verbose bool) fn.Deployer {
options := []keda.DeployerOpt{
return keda.NewDeployer(
keda.WithDeployerVerbose(verbose),
keda.WithDeployerDecorator(deployDecorator{}),
}

return keda.NewDeployer(options...)
keda.WithExposer(ocproute.New(deployers.Keda)),
)
}

type deployDecorator struct {
Expand Down
20 changes: 20 additions & 0 deletions cmd/completion_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,23 @@ func CompleteDeployerList(cmd *cobra.Command, args []string, complete string) (m

return
}

func CompleteExposeList(cmd *cobra.Command, args []string, complete string) (matches []string, d cobra.ShellCompDirective) {
values := fn.ExposeModes

d = cobra.ShellCompDirectiveNoFileComp
matches = []string{}

if len(complete) == 0 {
matches = values
return
}

for _, v := range values {
if strings.HasPrefix(v, complete) {
matches = append(matches, v)
}
}

return
}
Loading
Loading