Compare commits

..

35 Commits

Author SHA1 Message Date
9245a4aa87 更新 .github/workflows/build.yaml
All checks were successful
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Successful in 14m16s
2025-10-03 03:21:47 +00:00
c86c5414f3 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Has been cancelled
2025-10-03 03:10:26 +00:00
8769b4d7f8 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Has been cancelled
2025-10-03 03:00:55 +00:00
9cba926550 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Has been cancelled
2025-10-03 02:35:45 +00:00
79f5054804 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Has been cancelled
2025-10-03 02:30:24 +00:00
0aebf31939 更新 .github/workflows/build.yaml 2025-10-03 02:26:54 +00:00
cc86998f2c 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Failing after 32s
2025-10-03 02:17:15 +00:00
5480aa25b8 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Has been cancelled
2025-10-03 02:13:12 +00:00
795b7e399d 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Failing after 29s
2025-10-03 01:40:22 +00:00
8bebd56cf1 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Failing after 33s
2025-10-03 01:27:10 +00:00
76374a698a 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Has been cancelled
2025-10-03 01:23:29 +00:00
63626c2ab9 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Has been cancelled
2025-10-03 01:06:31 +00:00
96fd02b85f 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Has been cancelled
2025-10-03 01:01:26 +00:00
bcb52e3548 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Failing after 31s
2025-10-03 00:55:28 +00:00
084cc375da 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Failing after 28s
2025-10-03 00:41:50 +00:00
fa02b48f15 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Failing after 30s
2025-10-03 00:39:35 +00:00
422f8c4871 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Failing after 36s
2025-10-03 00:35:20 +00:00
eb6869404e 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Has been cancelled
2025-10-02 23:58:22 +00:00
34409f04a3 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Has been cancelled
2025-10-02 23:52:12 +00:00
b47ed9f622 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Failing after 59s
2025-10-02 23:43:59 +00:00
9c678d50e6 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Failing after 1m1s
2025-10-02 23:31:02 +00:00
b683bfbcc0 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Failing after 20s
2025-10-02 23:29:18 +00:00
68095c2434 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Failing after 15s
2025-10-02 23:14:48 +00:00
c8d463dcb8 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Failing after 18s
2025-10-02 23:11:21 +00:00
11525cf938 更新 .github/workflows/build.yaml 2025-10-02 23:08:28 +00:00
1afb6a2260 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Failing after 55s
2025-10-02 23:03:39 +00:00
12784de8d3 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Failing after 57s
2025-10-02 17:01:01 +00:00
5c71e3c2c5 alpine
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Failing after 55s
2025-07-01 02:34:40 +00:00
27576e9557 删除 .github/workflows/codeql-analysis.yml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Has been cancelled
2025-07-01 02:27:31 +00:00
2e357d1988 删除 .github/workflows/ci.yml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled
2025-07-01 02:27:16 +00:00
bd151cea10 更新 Dockerfile
Some checks failed
CI / Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Envtest Tests (push) Has been cancelled
CI / E2E (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Has been cancelled
2025-07-01 02:17:49 +00:00
f1ff24e1c4 更新 Dockerfile
Some checks failed
CI / Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Envtest Tests (push) Has been cancelled
CI / E2E (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Has been cancelled
2025-07-01 02:08:47 +00:00
a34171c71e 更新 .github/workflows/build.yaml
Some checks failed
Virtual Kubelet Docker Build and Deploy / build-and-deploy (push) Successful in 7m47s
CI / Lint (push) Failing after 20m58s
CodeQL / Analyze (go) (push) Has been cancelled
CI / Unit Tests (push) Failing after 11m40s
CI / Envtest Tests (push) Failing after 3m53s
CI / E2E (push) Failing after 35s
2025-07-01 00:26:45 +00:00
08ddc9acf6 更新 .github/workflows/build.yaml
Some checks failed
CI / Unit Tests (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Envtest Tests (push) Has been cancelled
CI / E2E (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled
2025-07-01 00:24:59 +00:00
d2a043f2a3 添加 .github/workflows/build.yaml
Some checks failed
CI / Lint (push) Has started running
CI / Unit Tests (push) Has been cancelled
CI / Envtest Tests (push) Has been cancelled
CI / E2E (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled
2025-07-01 00:19:14 +00:00
15 changed files with 184 additions and 459 deletions

69
.github/workflows/build.yaml vendored Normal file
View File

@@ -0,0 +1,69 @@
name: Virtual Kubelet Docker Build and Deploy
on:
push:
branches: [master]
pull_request:
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: 输出触发信息
run: |
echo "🎉 该作业由 ${{ github.event_name }} 事件自动触发。"
echo "🐧 此作业当前在 GitHub 托管的 ${{ runner.os }} 服务器上运行!"
echo "🔎 您的发布版本是 ${{ github.ref_name }},仓库是 ${{ github.repository }}。"
- name: 检出仓库代码
uses: actions/checkout@v4
- name: 输出工作区信息
run: |
echo "💡 ${{ github.repository }} 仓库已克隆到运行器。"
echo "🖥️ 工作流现在已准备好在运行器上测试您的代码。"
ls -la ${{ github.workspace }}
echo "🍏 此作业的状态是 ${{ job.status }}。"
- name: 设置 Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver: docker # 明确指定使用 docker 驱动
- name: 从标签名中提取版本号
id: extract_version
run: |
# 从Release标签名中提取版本号例如从 v0.1.0 中提取 v0.1.0
VERSION=$(echo "${{ github.ref_name }}" | sed 's|refs/tags/||')
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "提取的版本号:$VERSION"
- name: 登录 Docker 注册表
uses: docker/login-action@v3
with:
registry: registry-vpc.cn-beijing.aliyuncs.com
username: ${{ secrets.ALI_DOCKER_REGISTRY_USERNAME }}
password: ${{ secrets.ALI_DOCKER_REGISTRY_PASSWORD }}
- name: 构建并推送 Virtual Kubelet 镜像
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile # 确保Dockerfile路径正确
push: true
tags: |
registry-vpc.cn-beijing.aliyuncs.com/d8dcloud/virtual-kubelet:alpine
build-args: |
GOLANG_CI_LINT_VERSION=v1.68.0
BUILD_TAGS=
HTTP_PROXY=${{ vars.HTTP_PROXY }}
HTTPS_PROXY=${{ vars.HTTPS_PROXY }}
NO_PROXY=${{ vars.NO_PROXY }}
labels: |
org.opencontainers.image.title=Virtual Kubelet
org.opencontainers.image.description=Kubernetes Virtual Kubelet implementation
org.opencontainers.image.version=${{ env.VERSION }}
org.opencontainers.image.source=${{ github.repositoryUrl }}
org.opencontainers.image.revision=${{ github.sha }}

View File

@@ -1,115 +0,0 @@
name: CI
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches: [master]
pull_request:
permissions:
contents: read
env:
GO_VERSION: "1.23"
jobs:
lint:
name: Lint
runs-on: ubuntu-22.04
timeout-minutes: 20
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache: false
- uses: actions/checkout@v4
- uses: golangci/golangci-lint-action@v8
with:
version: v2.1
args: --timeout=15m --config=.golangci.yml
skip-cache: true
unit-tests:
name: Unit Tests
runs-on: ubuntu-22.04
timeout-minutes: 20
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- uses: actions/checkout@v4
- name: Run Tests
run: make test
env-tests:
name: Envtest Tests
runs-on: ubuntu-22.04
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- uses: actions/checkout@v4
- name: Run Tests
run: make envtest
e2e:
name: E2E
runs-on: ubuntu-22.04
timeout-minutes: 20
env:
CHANGE_MINIKUBE_NONE_USER: true
KUBERNETES_VERSION: v1.31
MINIKUBE_HOME: /home/runner
MINIKUBE_VERSION: v1.34.0
MINIKUBE_WANTUPDATENOTIFICATION: false
MINIKUBE_WANTREPORTERRORPROMPT: false
SKAFFOLD_VERSION: v2.13.2
GO111MODULE: "on"
steps:
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Skaffold
run: |
curl -sLo skaffold https://storage.googleapis.com/skaffold/releases/${SKAFFOLD_VERSION}/skaffold-linux-amd64
chmod +x skaffold
sudo mv skaffold /usr/local/bin/
echo /usr/local/bin >> $GITHUB_PATH
- name: Install Minikube dependencies
run: |
sudo apt-get update && sudo apt-get install -y apt-transport-https curl
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/${KUBERNETES_VERSION}/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
curl -fsSL https://pkgs.k8s.io/core:/stable:/${KUBERNETES_VERSION}/deb/Release.key | sudo gpg --no-tty --yes --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
sudo apt-get update
sudo apt-get remove -y containerd.io containerd
sudo apt-get install -y kubectl docker.io
- name: Install Minikube
run: |
curl -sLo minikube https://storage.googleapis.com/minikube/releases/${MINIKUBE_VERSION}/minikube-linux-amd64
chmod +x minikube
sudo mv minikube /usr/local/bin/
- name: Start Minikube
run: |
sudo usermod -aG docker $USER && newgrp docker
minikube start --vm-driver=docker --cpus 2 --memory 2048 --kubernetes-version=${KUBERNETES_VERSION}
- name: Wait for Minikube
run: |
JSONPATH='{range .items[*]}{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status};{end}{end}';
until kubectl get nodes -o jsonpath="$JSONPATH" 2>&1 | grep -q "Ready=True"; do
sleep 1;
done
- name: Run Tests
run: make e2e

View File

@@ -1,59 +0,0 @@
name: "CodeQL"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches: [master]
schedule:
- cron: "19 18 * * 3"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
security-events: write
strategy:
fail-fast: false
matrix:
language: ["go"]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3

View File

@@ -25,8 +25,16 @@ RUN \
--mount=type=cache,target=/root/.cache/golangci-lint \
golangci-lint run -v --out-format="${OUT_FORMAT:-colored-line-number}"
FROM scratch
# FROM scratch
# COPY --from=builder /usr/bin/virtual-kubelet /usr/bin/virtual-kubelet
# COPY --from=builder /etc/ssl/certs/ /etc/ssl/certs
FROM alpine:latest
COPY --from=builder /usr/bin/virtual-kubelet /usr/bin/virtual-kubelet
COPY --from=builder /etc/ssl/certs/ /etc/ssl/certs
# 创建必要的目录
RUN mkdir -p /root/.kube
ENTRYPOINT [ "/usr/bin/virtual-kubelet" ]
CMD [ "--help" ]

View File

@@ -16,21 +16,31 @@ package root
import (
"fmt"
"os"
"time"
)
type apiServerConfig struct {
CertPath string
KeyPath string
CACertPath string
Addr string
MetricsAddr string
StreamIdleTimeout time.Duration
StreamCreationTimeout time.Duration
}
func getAPIConfig(c Opts) apiServerConfig {
return apiServerConfig{
Addr: fmt.Sprintf(":%d", c.ListenPort),
MetricsAddr: c.MetricsAddr,
StreamIdleTimeout: c.StreamIdleTimeout,
StreamCreationTimeout: c.StreamCreationTimeout,
func getAPIConfig(c Opts) (*apiServerConfig, error) {
config := apiServerConfig{
CertPath: os.Getenv("APISERVER_CERT_LOCATION"),
KeyPath: os.Getenv("APISERVER_KEY_LOCATION"),
CACertPath: os.Getenv("APISERVER_CA_CERT_LOCATION"),
}
config.Addr = fmt.Sprintf(":%d", c.ListenPort)
config.MetricsAddr = c.MetricsAddr
config.StreamIdleTimeout = c.StreamIdleTimeout
config.StreamCreationTimeout = c.StreamCreationTimeout
return &config, nil
}

View File

@@ -16,6 +16,8 @@ package root
import (
"context"
"crypto/tls"
"net/http"
"os"
"runtime"
@@ -26,8 +28,10 @@ import (
"github.com/virtual-kubelet/virtual-kubelet/internal/manager"
"github.com/virtual-kubelet/virtual-kubelet/log"
"github.com/virtual-kubelet/virtual-kubelet/node"
"github.com/virtual-kubelet/virtual-kubelet/node/api"
"github.com/virtual-kubelet/virtual-kubelet/node/nodeutil"
corev1 "k8s.io/api/core/v1"
"k8s.io/apiserver/pkg/server/dynamiccertificates"
)
// NewCommand creates a new top-level command.
@@ -69,6 +73,14 @@ func runRootCommand(ctx context.Context, s *provider.Store, c Opts) error {
}
}
// Ensure API client.
clientSet, err := nodeutil.ClientsetFromEnv(c.KubeConfigPath)
if err != nil {
return err
}
// Set-up the node provider.
mux := http.NewServeMux()
newProvider := func(cfg nodeutil.ProviderConfig) (nodeutil.Provider, node.NodeProvider, error) {
rm, err := manager.NewResourceManager(cfg.Pods, cfg.Secrets, cfg.ConfigMaps, cfg.Services)
if err != nil {
@@ -97,9 +109,14 @@ func runRootCommand(ctx context.Context, s *provider.Store, c Opts) error {
return p, nil, nil
}
apiConfig := getAPIConfig(c)
apiConfig, err := getAPIConfig(c)
if err != nil {
return err
}
cm, err := nodeutil.NewNode(c.NodeName, newProvider, func(cfg *nodeutil.NodeConfig) error {
cfg.KubeconfigPath = c.KubeConfigPath
cfg.Handler = mux
cfg.InformerResyncPeriod = c.InformerResyncPeriod
if taint != nil {
@@ -117,7 +134,13 @@ func runRootCommand(ctx context.Context, s *provider.Store, c Opts) error {
return nil
},
nodeutil.WithBootstrapFromRestConfig(),
nodeutil.WithClient(clientSet),
setAuth(c.NodeName, apiConfig),
nodeutil.WithTLSConfig(
nodeutil.WithKeyPairFromPath(apiConfig.CertPath, apiConfig.KeyPath),
maybeCA(apiConfig.CACertPath),
),
nodeutil.AttachProviderRoutes(mux),
)
if err != nil {
return err
@@ -156,3 +179,32 @@ func runRootCommand(ctx context.Context, s *provider.Store, c Opts) error {
}
return nil
}
func setAuth(node string, apiCfg *apiServerConfig) nodeutil.NodeOpt {
if apiCfg.CACertPath == "" {
return func(cfg *nodeutil.NodeConfig) error {
cfg.Handler = api.InstrumentHandler(nodeutil.WithAuth(nodeutil.NoAuth(), cfg.Handler))
return nil
}
}
return func(cfg *nodeutil.NodeConfig) error {
auth, err := nodeutil.WebhookAuth(cfg.Client, node, func(cfg *nodeutil.WebhookAuthConfig) error {
var err error
cfg.AuthnConfig.ClientCertificateCAContentProvider, err = dynamiccertificates.NewDynamicCAContentFromFile("ca-cert-bundle", apiCfg.CACertPath)
return err
})
if err != nil {
return err
}
cfg.Handler = api.InstrumentHandler(nodeutil.WithAuth(auth, cfg.Handler))
return nil
}
}
func maybeCA(p string) func(*tls.Config) error {
if p == "" {
return func(*tls.Config) error { return nil }
}
return nodeutil.WithCAFromPath(p)
}

View File

@@ -102,7 +102,7 @@ func NewMockProvider(providerConfig, nodeName, operatingSystem string, internalI
func loadConfig(providerConfig, nodeName string) (config MockConfig, err error) {
data, err := os.ReadFile(providerConfig)
if err != nil {
return config, fmt.Errorf("error reaeding provider config: %w", err)
return config, err
}
configMap := map[string]MockConfig{}
err = json.Unmarshal(data, &configMap)

View File

@@ -1,33 +0,0 @@
package errdefs
import (
"errors"
"fmt"
)
var (
// ErrForbidden is returned when the user is not authorized to perform the operation.
ErrForbidden = errors.New("forbidden")
// ErrUnauthorized is returned when the user is not authenticated.
ErrUnauthorized = errors.New("unauthorized")
)
// Unauthorized wraps ErrUnauthorized with a message.
func Unauthorized(msg string) error {
return fmt.Errorf("%w: %s", ErrUnauthorized, msg)
}
// Forbidden wraps ErrForbidden with a message.
func Forbidden(msg string) error {
return fmt.Errorf("%w: %s", ErrForbidden, msg)
}
// IsForbidden returns true if the error has ErrForbidden in the error chain.
func IsForbidden(err error) bool {
return errors.Is(err, ErrForbidden)
}
// IsUnauthorized returns true if the error has ErrUnauthorized in the error chain.
func IsUnauthorized(err error) bool {
return errors.Is(err, ErrUnauthorized)
}

View File

@@ -48,7 +48,6 @@ func HandlePodStatsSummary(h PodStatsSummaryHandlerFunc) http.HandlerFunc {
if _, err := w.Write(b); err != nil {
return errors.Wrap(err, "could not write to client")
}
_, _ = w.Write([]byte("\n"))
return nil
})
}

View File

@@ -6,7 +6,6 @@ import (
"strings"
"time"
"github.com/virtual-kubelet/virtual-kubelet/errdefs"
"github.com/virtual-kubelet/virtual-kubelet/log"
"github.com/virtual-kubelet/virtual-kubelet/trace"
"k8s.io/apiserver/pkg/authentication/authenticator"
@@ -70,24 +69,14 @@ func handleAuth(auth Auth, w http.ResponseWriter, r *http.Request, next http.Han
defer span.End()
r = r.WithContext(ctx)
logger := log.G(r.Context())
info, ok, err := auth.AuthenticateRequest(r)
if err != nil {
logger.WithError(err).Error("Error authenticating request")
if err != nil || !ok {
log.G(r.Context()).WithError(err).Error("Authorization error")
http.Error(w, "Unauthorized", http.StatusUnauthorized)
span.SetStatus(err)
return
}
if !ok {
logger.Error("Request not authenticated")
log.G(r.Context()).Infof("Unauthorized: RequestURI: %s", r.RequestURI)
http.Error(w, "Unauthorized", http.StatusUnauthorized)
span.SetStatus(errdefs.ErrUnauthorized)
return
}
logger = logger.WithFields(log.Fields{
logger := log.G(ctx).WithFields(log.Fields{
"user-name": info.User.GetName(),
"user-id": info.User.GetUID(),
})
@@ -101,13 +90,11 @@ func handleAuth(auth Auth, w http.ResponseWriter, r *http.Request, next http.Han
if err != nil {
log.G(r.Context()).WithError(err).Error("Authorization error")
http.Error(w, err.Error(), http.StatusInternalServerError)
span.SetStatus(err)
return
}
if decision != authorizer.DecisionAllow {
http.Error(w, "Forbidden", http.StatusForbidden)
span.SetStatus(errdefs.ErrForbidden)
return
}

View File

@@ -24,7 +24,7 @@ func ClientsetFromEnv(kubeConfigPath string) (*kubernetes.Clientset, error) {
)
if kubeConfigPath != "" {
config, err = RestConfigFromEnv(kubeConfigPath)
config, err = clientsetFromEnvKubeConfigPath(kubeConfigPath)
} else {
config, err = rest.InClusterConfig()
}
@@ -36,8 +36,7 @@ func ClientsetFromEnv(kubeConfigPath string) (*kubernetes.Clientset, error) {
return kubernetes.NewForConfig(config)
}
// RestConfigFromEnv is like ClientsetFromEnv except it returns a rest config instead of a full client.
func RestConfigFromEnv(kubeConfigPath string) (*rest.Config, error) {
func clientsetFromEnvKubeConfigPath(kubeConfigPath string) (*rest.Config, error) {
_, err := os.Stat(kubeConfigPath)
if os.IsNotExist(err) {
return rest.InClusterConfig()

View File

@@ -48,8 +48,7 @@ type Node struct {
workers int
eb record.EventBroadcaster
caController caController
eb record.EventBroadcaster
}
// NodeController returns the configured node controller.
@@ -108,10 +107,6 @@ func (n *Node) Run(ctx context.Context) (retErr error) {
log.G(ctx).Debug("Started event broadcaster")
}
if n.caController != nil {
go n.caController.Run(ctx, 1)
}
cancelHTTP, err := n.runHTTP(ctx)
if err != nil {
return err
@@ -217,8 +212,6 @@ func (n *Node) Err() error {
// NodeOpt is used as functional options when configuring a new node in NewNodeFromClient
type NodeOpt func(c *NodeConfig) error
type caController interface{ Run(context.Context, int) }
// NodeConfig is used to hold configuration items for a Node.
// It gets used in conjection with NodeOpt in NewNodeFromClient
type NodeConfig struct {
@@ -267,8 +260,22 @@ type NodeConfig struct {
SkipDownwardAPIResolution bool
routeAttacher func(Provider, NodeConfig, corev1listers.PodLister)
}
caController caController
// WithNodeConfig returns a NodeOpt which replaces the NodeConfig with the passed in value.
func WithNodeConfig(c NodeConfig) NodeOpt {
return func(orig *NodeConfig) error {
*orig = c
return nil
}
}
// WithClient return a NodeOpt that sets the client that will be used to create/manage the node.
func WithClient(c kubernetes.Interface) NodeOpt {
return func(cfg *NodeConfig) error {
cfg.Client = c
return nil
}
}
// NewNode creates a new node using the provided client and name.
@@ -319,6 +326,10 @@ func NewNode(name string, newProvider NewProviderFunc, opts ...NodeOpt) (*Node,
return nil, errors.Wrap(err, "error parsing http listen address")
}
if cfg.Client == nil {
return nil, errors.New("no client provided")
}
podInformerFactory := informers.NewSharedInformerFactoryWithOptions(
cfg.Client,
cfg.InformerResyncPeriod,
@@ -414,7 +425,6 @@ func NewNode(name string, newProvider NewProviderFunc, opts ...NodeOpt) (*Node,
h: cfg.Handler,
listenAddr: cfg.HTTPListenAddr,
workers: cfg.NumWorkers,
caController: cfg.caController,
}, nil
}

View File

@@ -1,150 +0,0 @@
package nodeutil
import (
"fmt"
"net/http"
"github.com/virtual-kubelet/virtual-kubelet/errdefs"
"github.com/virtual-kubelet/virtual-kubelet/node/api"
"k8s.io/apiserver/pkg/server/dynamiccertificates"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
// WithNodeConfig returns a NodeOpt which replaces the NodeConfig with the passed in value.
func WithNodeConfig(c NodeConfig) NodeOpt {
return func(orig *NodeConfig) error {
*orig = c
return nil
}
}
// WithClient return a NodeOpt that sets the client that will be used to create/manage the node.
func WithClient(c kubernetes.Interface) NodeOpt {
return func(cfg *NodeConfig) error {
cfg.Client = c
return nil
}
}
// BootstrapConfig is the configuration for bootstrapping a node.
type BootstrapConfig struct {
// ClientCAConfigMapSpec is the config map information for getting the CA for authenticating clients
// If not set, the CA in the rest config will be used.
ClientCAConfigMapSpec *ConfigMapCASpec
// A set of options to pass when creating the webhook auth.
WebhookAuthOpts []WebhookAuthOption
// RestConfig is the rest config to use for the client
// If it is not provided a default one from the environment will be created.
RestConfig *rest.Config
}
// ConfigMapCASpec is the spec for a config map that contains a CA.
type ConfigMapCASpec struct {
Namespace string
Name string
Key string
}
// BootstrapOpt is a functional option used to configure node bootstrapping
type BootstrapOpt func(*BootstrapConfig) error
// WithBootstrapFromRestConfig takes a reset config (or a default one from the environment) and returns a NodeOpt that will:
// 1. Create a client from the rest config (if no client is set)
// 2. Create webhook authn/authz from the rest config
// 3. Configure the TLS config for the HTTP server from the certs in the rest config.
func WithBootstrapFromRestConfig(opts ...BootstrapOpt) NodeOpt {
return func(cfg *NodeConfig) error {
var bOpts BootstrapConfig
if rCfg, err := RestConfigFromEnv(cfg.KubeconfigPath); err == nil {
bOpts.RestConfig = rCfg
}
for _, o := range opts {
if err := o(&bOpts); err != nil {
return err
}
}
if bOpts.RestConfig == nil {
return fmt.Errorf("no rest config provided")
}
if cfg.Client == nil {
client, err := kubernetes.NewForConfig(bOpts.RestConfig)
if err != nil {
return err
}
cfg.Client = client
}
if err := WithTLSConfig(tlsFromRestConfig(bOpts.RestConfig))(cfg); err != nil {
return err
}
if err := configureWebhookCA(cfg, &bOpts); err != nil {
return fmt.Errorf("error configure webhook auth: %w", err)
}
var mux *http.ServeMux
if cfg.Handler == nil {
mux = http.NewServeMux()
cfg.Handler = mux
}
if cfg.routeAttacher == nil && mux != nil {
if err := AttachProviderRoutes(mux)(cfg); err != nil {
return err
}
}
auth, err := WebhookAuth(cfg.Client, cfg.NodeSpec.Name, bOpts.WebhookAuthOpts...)
if err != nil {
return err
}
cfg.Handler = api.InstrumentHandler(WithAuth(auth, cfg.Handler))
return nil
}
}
func configureWebhookCA(cfg *NodeConfig, bCfg *BootstrapConfig) error {
if bCfg.ClientCAConfigMapSpec != nil {
bCfg.WebhookAuthOpts = append(bCfg.WebhookAuthOpts, func(auth *WebhookAuthConfig) error {
cmCA, err := dynamiccertificates.NewDynamicCAFromConfigMapController("client-ca", bCfg.ClientCAConfigMapSpec.Namespace, bCfg.ClientCAConfigMapSpec.Name, bCfg.ClientCAConfigMapSpec.Key, cfg.Client)
if err != nil {
return fmt.Errorf("error loading dynamic CA from config map: %w", err)
}
auth.AuthnConfig.ClientCertificateCAContentProvider = cmCA
cfg.caController = cmCA
return nil
})
return nil
}
if bCfg.RestConfig.CAFile != "" {
bCfg.WebhookAuthOpts = append(bCfg.WebhookAuthOpts, func(auth *WebhookAuthConfig) error {
caFile, err := dynamiccertificates.NewDynamicCAContentFromFile("ca-file", bCfg.RestConfig.CAFile)
if err != nil {
return fmt.Errorf("error loading dynamic CA file from rest config: %w", err)
}
auth.AuthnConfig.ClientCertificateCAContentProvider = caFile
cfg.caController = caFile
return nil
})
return nil
}
if bCfg.RestConfig.CAData != nil {
bCfg.WebhookAuthOpts = append(bCfg.WebhookAuthOpts, func(auth *WebhookAuthConfig) error {
caData, err := dynamiccertificates.NewStaticCAContent("ca-data", bCfg.RestConfig.CAData)
if err != nil {
return fmt.Errorf("error loading static ca from rest config: %w", err)
}
auth.AuthnConfig.ClientCertificateCAContentProvider = caData
return nil
})
return nil
}
return errdefs.InvalidInput("no client CA found")
}

View File

@@ -5,28 +5,25 @@ import (
"crypto/x509"
"fmt"
"os"
"k8s.io/client-go/rest"
)
// WithTLSConfig returns a NodeOpt which creates a base TLSConfig with the default cipher suites and tls min verions.
// The tls config can be modified through functional options.
func WithTLSConfig(opts ...func(*tls.Config) error) NodeOpt {
return func(cfg *NodeConfig) error {
if cfg.TLSConfig == nil {
cfg.TLSConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
PreferServerCipherSuites: true,
CipherSuites: DefaultServerCiphers(),
ClientAuth: tls.RequestClientCert,
}
tlsCfg := &tls.Config{
MinVersion: tls.VersionTLS12,
PreferServerCipherSuites: true,
CipherSuites: DefaultServerCiphers(),
ClientAuth: tls.RequestClientCert,
}
for _, o := range opts {
if err := o(cfg.TLSConfig); err != nil {
if err := o(tlsCfg); err != nil {
return err
}
}
cfg.TLSConfig = tlsCfg
return nil
}
}
@@ -48,7 +45,7 @@ func WithKeyPairFromPath(cert, key string) func(*tls.Config) error {
return func(cfg *tls.Config) error {
cert, err := tls.LoadX509KeyPair(cert, key)
if err != nil {
return fmt.Errorf("error loading x509 key pair: %w", err)
return err
}
cfg.Certificates = append(cfg.Certificates, cert)
return nil
@@ -59,7 +56,7 @@ func WithKeyPairFromPath(cert, key string) func(*tls.Config) error {
// If a cert pool is not defined on the tls config an empty one will be created.
func WithCACert(pem []byte) func(*tls.Config) error {
return func(cfg *tls.Config) error {
if cfg.RootCAs == nil {
if cfg.ClientCAs == nil {
cfg.ClientCAs = x509.NewCertPool()
}
if !cfg.ClientCAs.AppendCertsFromPEM(pem) {
@@ -84,48 +81,3 @@ func DefaultServerCiphers() []uint16 {
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
}
}
func tlsFromRestConfig(r *rest.Config) func(*tls.Config) error {
return func(cfg *tls.Config) error {
var err error
cfg.ClientAuth = tls.RequestClientCert
certData := r.CertData
if certData == nil && r.CertFile != "" {
certData, err = os.ReadFile(r.CertFile)
if err != nil {
return fmt.Errorf("error reading cert file from clientset: %w", err)
}
}
keyData := r.KeyData
if keyData == nil && r.KeyFile != "" {
keyData, err = os.ReadFile(r.KeyFile)
if err != nil {
return fmt.Errorf("error reading key file from clientset: %w", err)
}
}
if keyData != nil && certData != nil {
pem, err := tls.X509KeyPair(certData, keyData)
if err != nil {
return fmt.Errorf("error creating key pair from clientset: %w", err)
}
cfg.Certificates = append(cfg.Certificates, pem)
cfg.ClientAuth = tls.RequestClientCert
}
caData := r.CAData
if certData == nil && r.CAFile != "" {
caData, err = os.ReadFile(r.CAFile)
if err != nil {
return fmt.Errorf("error reading ca file from clientset: %w", err)
}
}
if caData != nil {
return WithCACert(caData)(cfg)
}
return nil
}
}

View File

@@ -79,12 +79,8 @@ func (s *span) SetStatus(err error) {
status.Code = octrace.StatusCodeNotFound
case errdefs.IsInvalidInput(err):
status.Code = octrace.StatusCodeInvalidArgument
case errdefs.IsForbidden(err):
status.Code = octrace.StatusCodePermissionDenied
case errdefs.IsUnauthorized(err):
status.Code = octrace.StatusCodeUnauthenticated
default:
// TODO: other error types
default:
status.Code = octrace.StatusCodeUnknown
}