Decouple vkubelet/* packages from providers (#626)

This makes the concept of a `Provider` wholely implemented in the cli
implementation in cmd/virtual-kubelet.

It allows us to slim down the interfaces used in vkubelet (and
vkubelet/api) to what is actually used there rather than a huge
interface that is only there to serve the CLI's needs.
This commit is contained in:
Brian Goff
2019-05-17 17:01:05 -07:00
committed by GitHub
parent 87e72bf4df
commit 7dd49516d8
26 changed files with 217 additions and 194 deletions

View File

@@ -26,7 +26,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/virtual-kubelet/virtual-kubelet/log" "github.com/virtual-kubelet/virtual-kubelet/log"
"github.com/virtual-kubelet/virtual-kubelet/providers" "github.com/virtual-kubelet/virtual-kubelet/providers"
"github.com/virtual-kubelet/virtual-kubelet/vkubelet" "github.com/virtual-kubelet/virtual-kubelet/vkubelet/api"
) )
// AcceptedCiphers is the list of accepted TLS ciphers, with known weak ciphers elided // AcceptedCiphers is the list of accepted TLS ciphers, with known weak ciphers elided
@@ -86,7 +86,13 @@ func setupHTTPServer(ctx context.Context, p providers.Provider, cfg *apiServerCo
} }
mux := http.NewServeMux() mux := http.NewServeMux()
vkubelet.AttachPodRoutes(p, mux, true)
podRoutes := api.PodHandlerConfig{
RunInContainer: p.RunInContainer,
GetContainerLogs: p.GetContainerLogs,
GetPods: p.GetPods,
}
api.AttachPodRoutes(podRoutes, mux, true)
s := &http.Server{ s := &http.Server{
Handler: mux, Handler: mux,
@@ -105,7 +111,15 @@ func setupHTTPServer(ctx context.Context, p providers.Provider, cfg *apiServerCo
} }
mux := http.NewServeMux() mux := http.NewServeMux()
vkubelet.AttachMetricsRoutes(p, mux)
var summaryHandlerFunc api.PodStatsSummaryHandlerFunc
if mp, ok := p.(providers.PodMetricsProvider); ok {
summaryHandlerFunc = mp.GetStatsSummary
}
podMetricsRoutes := api.PodMetricsConfig{
GetStatsSummary: summaryHandlerFunc,
}
api.AttachPodMetricsRoutes(podMetricsRoutes, mux)
s := &http.Server{ s := &http.Server{
Handler: mux, Handler: mux,
} }

View File

@@ -20,8 +20,8 @@ import (
"github.com/cpuguy83/strongerrors" "github.com/cpuguy83/strongerrors"
"github.com/virtual-kubelet/virtual-kubelet/log" "github.com/virtual-kubelet/virtual-kubelet/log"
"github.com/virtual-kubelet/virtual-kubelet/manager" "github.com/virtual-kubelet/virtual-kubelet/manager"
"github.com/virtual-kubelet/virtual-kubelet/providers"
"github.com/virtual-kubelet/virtual-kubelet/providers/alibabacloud/eci" "github.com/virtual-kubelet/virtual-kubelet/providers/alibabacloud/eci"
"github.com/virtual-kubelet/virtual-kubelet/vkubelet/api"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors" k8serr "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
@@ -279,7 +279,7 @@ func (p *ECIProvider) GetPod(ctx context.Context, namespace, name string) (*v1.P
} }
// GetContainerLogs returns the logs of a pod by name that is running inside ECI. // GetContainerLogs returns the logs of a pod by name that is running inside ECI.
func (p *ECIProvider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts providers.ContainerLogOpts) (io.ReadCloser, error) { func (p *ECIProvider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts api.ContainerLogOpts) (io.ReadCloser, error) {
eciId := "" eciId := ""
for _, cg := range p.GetCgs() { for _, cg := range p.GetCgs() {
if getECITagValue(&cg, "PodName") == podName && getECITagValue(&cg, "NameSpace") == namespace { if getECITagValue(&cg, "PodName") == podName && getECITagValue(&cg, "NameSpace") == namespace {
@@ -321,7 +321,7 @@ func (p *ECIProvider) GetPodFullName(namespace string, pod string) string {
// RunInContainer executes a command in a container in the pod, copying data // RunInContainer executes a command in a container in the pod, copying data
// between in/out/err and the container's stdin/stdout/stderr. // between in/out/err and the container's stdin/stdout/stderr.
func (p *ECIProvider) RunInContainer(ctx context.Context, namespace, podName, containerName string, cmd []string, attach providers.AttachIO) error { func (p *ECIProvider) RunInContainer(ctx context.Context, namespace, podName, containerName string, cmd []string, attach api.AttachIO) error {
return nil return nil
} }

View File

@@ -13,7 +13,7 @@ import (
"github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go/service/ecs" "github.com/aws/aws-sdk-go/service/ecs"
"github.com/cpuguy83/strongerrors" "github.com/cpuguy83/strongerrors"
"github.com/virtual-kubelet/virtual-kubelet/providers" "github.com/virtual-kubelet/virtual-kubelet/vkubelet/api"
k8sTypes "k8s.io/apimachinery/pkg/types" k8sTypes "k8s.io/apimachinery/pkg/types"
) )
@@ -313,7 +313,7 @@ func (c *Cluster) RemovePod(tag string) {
} }
// GetContainerLogs returns the logs of a container from this cluster. // GetContainerLogs returns the logs of a container from this cluster.
func (c *Cluster) GetContainerLogs(namespace, podName, containerName string, opts providers.ContainerLogOpts) (io.ReadCloser, error) { func (c *Cluster) GetContainerLogs(namespace, podName, containerName string, opts api.ContainerLogOpts) (io.ReadCloser, error) {
if c.cloudWatchLogGroupName == "" { if c.cloudWatchLogGroupName == "" {
return nil, fmt.Errorf("logs not configured, please specify a \"CloudWatchLogGroupName\"") return nil, fmt.Errorf("logs not configured, please specify a \"CloudWatchLogGroupName\"")
} }

View File

@@ -8,8 +8,8 @@ import (
"time" "time"
"github.com/virtual-kubelet/virtual-kubelet/manager" "github.com/virtual-kubelet/virtual-kubelet/manager"
"github.com/virtual-kubelet/virtual-kubelet/providers"
"github.com/virtual-kubelet/virtual-kubelet/providers/aws/fargate" "github.com/virtual-kubelet/virtual-kubelet/providers/aws/fargate"
"github.com/virtual-kubelet/virtual-kubelet/vkubelet/api"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -172,7 +172,7 @@ func (p *FargateProvider) GetPod(ctx context.Context, namespace, name string) (*
} }
// GetContainerLogs retrieves the logs of a container by name from the provider. // GetContainerLogs retrieves the logs of a container by name from the provider.
func (p *FargateProvider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts providers.ContainerLogOpts) (io.ReadCloser, error) { func (p *FargateProvider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts api.ContainerLogOpts) (io.ReadCloser, error) {
log.Printf("Received GetContainerLogs request for %s/%s/%s.\n", namespace, podName, containerName) log.Printf("Received GetContainerLogs request for %s/%s/%s.\n", namespace, podName, containerName)
return p.cluster.GetContainerLogs(namespace, podName, containerName, opts) return p.cluster.GetContainerLogs(namespace, podName, containerName, opts)
} }
@@ -184,7 +184,7 @@ func (p *FargateProvider) GetPodFullName(namespace string, pod string) string {
// RunInContainer executes a command in a container in the pod, copying data // RunInContainer executes a command in a container in the pod, copying data
// between in/out/err and the container's stdin/stdout/stderr. // between in/out/err and the container's stdin/stdout/stderr.
func (p *FargateProvider) RunInContainer(ctx context.Context, namespace, podName, containerName string, cmd []string, attach providers.AttachIO) error { func (p *FargateProvider) RunInContainer(ctx context.Context, namespace, podName, containerName string, cmd []string, attach api.AttachIO) error {
return errNotImplemented return errNotImplemented
} }

View File

@@ -15,8 +15,8 @@ import (
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ecs" "github.com/aws/aws-sdk-go/service/ecs"
"github.com/aws/aws-sdk-go/service/iam" "github.com/aws/aws-sdk-go/service/iam"
"github.com/virtual-kubelet/virtual-kubelet/providers"
vkAWS "github.com/virtual-kubelet/virtual-kubelet/providers/aws" vkAWS "github.com/virtual-kubelet/virtual-kubelet/providers/aws"
"github.com/virtual-kubelet/virtual-kubelet/vkubelet/api"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -283,7 +283,7 @@ func TestAWSFargateProviderPodLifecycle(t *testing.T) {
// Wait a few seconds for the logs to settle. // Wait a few seconds for the logs to settle.
time.Sleep(10 * time.Second) time.Sleep(10 * time.Second)
logs, err := provider.GetContainerLogs(context.Background(), "default", podName, "echo-container", providers.ContainerLogOpts{Tail: 100}) logs, err := provider.GetContainerLogs(context.Background(), "default", podName, "echo-container", api.ContainerLogOpts{Tail: 100})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -26,8 +26,8 @@ import (
"github.com/virtual-kubelet/azure-aci/client/network" "github.com/virtual-kubelet/azure-aci/client/network"
"github.com/virtual-kubelet/virtual-kubelet/log" "github.com/virtual-kubelet/virtual-kubelet/log"
"github.com/virtual-kubelet/virtual-kubelet/manager" "github.com/virtual-kubelet/virtual-kubelet/manager"
"github.com/virtual-kubelet/virtual-kubelet/providers"
"github.com/virtual-kubelet/virtual-kubelet/trace" "github.com/virtual-kubelet/virtual-kubelet/trace"
"github.com/virtual-kubelet/virtual-kubelet/vkubelet/api"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors" k8serr "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
@@ -766,7 +766,7 @@ func (p *ACIProvider) GetPod(ctx context.Context, namespace, name string) (*v1.P
} }
// GetContainerLogs returns the logs of a pod by name that is running inside ACI. // GetContainerLogs returns the logs of a pod by name that is running inside ACI.
func (p *ACIProvider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts providers.ContainerLogOpts) (io.ReadCloser, error) { func (p *ACIProvider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts api.ContainerLogOpts) (io.ReadCloser, error) {
ctx, span := trace.StartSpan(ctx, "aci.GetContainerLogs") ctx, span := trace.StartSpan(ctx, "aci.GetContainerLogs")
defer span.End() defer span.End()
ctx = addAzureAttributes(ctx, span, p) ctx = addAzureAttributes(ctx, span, p)
@@ -804,7 +804,7 @@ func (p *ACIProvider) GetPodFullName(namespace string, pod string) string {
// RunInContainer executes a command in a container in the pod, copying data // RunInContainer executes a command in a container in the pod, copying data
// between in/out/err and the container's stdin/stdout/stderr. // between in/out/err and the container's stdin/stdout/stderr.
func (p *ACIProvider) RunInContainer(ctx context.Context, namespace, name, container string, cmd []string, attach providers.AttachIO) error { func (p *ACIProvider) RunInContainer(ctx context.Context, namespace, name, container string, cmd []string, attach api.AttachIO) error {
out := attach.Stdout() out := attach.Stdout()
if out != nil { if out != nil {
defer out.Close() defer out.Close()
@@ -816,7 +816,7 @@ func (p *ACIProvider) RunInContainer(ctx context.Context, namespace, name, conta
} }
// Set default terminal size // Set default terminal size
size := providers.TermSize{ size := api.TermSize{
Height: 60, Height: 60,
Width: 120, Width: 120,
} }

View File

@@ -17,8 +17,8 @@ import (
"github.com/Azure/go-autorest/autorest/to" "github.com/Azure/go-autorest/autorest/to"
"github.com/lawrencegripper/pod2docker" "github.com/lawrencegripper/pod2docker"
"github.com/virtual-kubelet/virtual-kubelet/manager" "github.com/virtual-kubelet/virtual-kubelet/manager"
"github.com/virtual-kubelet/virtual-kubelet/providers"
azureCreds "github.com/virtual-kubelet/virtual-kubelet/providers/azure" azureCreds "github.com/virtual-kubelet/virtual-kubelet/providers/azure"
"github.com/virtual-kubelet/virtual-kubelet/vkubelet/api"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -260,7 +260,7 @@ const (
) )
// GetContainerLogs returns the logs of a container running in a pod by name. // GetContainerLogs returns the logs of a container running in a pod by name.
func (p *Provider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts providers.ContainerLogOpts) (io.ReadCloser, error) { func (p *Provider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts api.ContainerLogOpts) (io.ReadCloser, error) {
log.Println("Getting pod logs ....") log.Println("Getting pod logs ....")
taskID := getTaskIDForPod(namespace, podName) taskID := getTaskIDForPod(namespace, podName)
@@ -319,7 +319,7 @@ func (p *Provider) GetPodFullName(namespace string, pod string) string {
// RunInContainer executes a command in a container in the pod, copying data // RunInContainer executes a command in a container in the pod, copying data
// between in/out/err and the container's stdin/stdout/stderr. // between in/out/err and the container's stdin/stdout/stderr.
// TODO: Implementation // TODO: Implementation
func (p *Provider) RunInContainer(ctx context.Context, namespace, name, container string, cmd []string, attach providers.AttachIO) error { func (p *Provider) RunInContainer(ctx context.Context, namespace, name, container string, cmd []string, attach api.AttachIO) error {
log.Printf("receive ExecInContainer %q\n", container) log.Printf("receive ExecInContainer %q\n", container)
return nil return nil
} }

View File

@@ -12,7 +12,7 @@ import (
"github.com/Azure/azure-sdk-for-go/services/batch/2017-09-01.6.0/batch" "github.com/Azure/azure-sdk-for-go/services/batch/2017-09-01.6.0/batch"
"github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest"
"github.com/virtual-kubelet/virtual-kubelet/providers" "github.com/virtual-kubelet/virtual-kubelet/vkubelet/api"
apiv1 "k8s.io/api/core/v1" apiv1 "k8s.io/api/core/v1"
) )
@@ -121,7 +121,7 @@ func Test_readLogs_404Response_expectReturnStartupLogs(t *testing.T) {
return batch.ReadCloser{}, fmt.Errorf("Failed in test mock of getFileFromTask") return batch.ReadCloser{}, fmt.Errorf("Failed in test mock of getFileFromTask")
} }
logs, err := provider.GetContainerLogs(context.Background(), pod.Namespace, pod.Name, containerName, providers.ContainerLogOpts{}) logs, err := provider.GetContainerLogs(context.Background(), pod.Namespace, pod.Name, containerName, api.ContainerLogOpts{})
if err != nil { if err != nil {
t.Fatalf("GetContainerLogs return error: %v", err) t.Fatalf("GetContainerLogs return error: %v", err)
} }
@@ -161,7 +161,7 @@ func Test_readLogs_JsonResponse_expectFormattedLogs(t *testing.T) {
return batch.ReadCloser{}, fmt.Errorf("Failed in test mock of getFileFromTask") return batch.ReadCloser{}, fmt.Errorf("Failed in test mock of getFileFromTask")
} }
logs, err := provider.GetContainerLogs(context.Background(), pod.Namespace, pod.Name, containerName, providers.ContainerLogOpts{}) logs, err := provider.GetContainerLogs(context.Background(), pod.Namespace, pod.Name, containerName, api.ContainerLogOpts{})
if err != nil { if err != nil {
t.Errorf("GetContainerLogs return error: %v", err) t.Errorf("GetContainerLogs return error: %v", err)
} }

View File

@@ -20,6 +20,7 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/virtual-kubelet/virtual-kubelet/manager" "github.com/virtual-kubelet/virtual-kubelet/manager"
"github.com/virtual-kubelet/virtual-kubelet/providers" "github.com/virtual-kubelet/virtual-kubelet/providers"
"github.com/virtual-kubelet/virtual-kubelet/vkubelet/api"
"google.golang.org/grpc" "google.golang.org/grpc"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors" k8serr "k8s.io/apimachinery/pkg/api/errors"
@@ -607,7 +608,7 @@ func (p *CRIProvider) GetPod(ctx context.Context, namespace, name string) (*v1.P
} }
// Reads a log file into a string // Reads a log file into a string
func readLogFile(filename string, opts providers.ContainerLogOpts) (io.ReadCloser, error) { func readLogFile(filename string, opts api.ContainerLogOpts) (io.ReadCloser, error) {
file, err := os.Open(filename) file, err := os.Open(filename)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -627,7 +628,7 @@ func readLogFile(filename string, opts providers.ContainerLogOpts) (io.ReadClose
} }
// Provider function to read the logs of a container // Provider function to read the logs of a container
func (p *CRIProvider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts providers.ContainerLogOpts) (io.ReadCloser, error) { func (p *CRIProvider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts api.ContainerLogOpts) (io.ReadCloser, error) {
log.Printf("receive GetContainerLogs %q", containerName) log.Printf("receive GetContainerLogs %q", containerName)
err := p.refreshNodeState() err := p.refreshNodeState()
@@ -656,7 +657,7 @@ func (p *CRIProvider) GetPodFullName(namespace string, pod string) string {
// RunInContainer executes a command in a container in the pod, copying data // RunInContainer executes a command in a container in the pod, copying data
// between in/out/err and the container's stdin/stdout/stderr. // between in/out/err and the container's stdin/stdout/stderr.
// TODO: Implementation // TODO: Implementation
func (p *CRIProvider) RunInContainer(ctx context.Context, namespace, name, container string, cmd []string, attach providers.AttachIO) error { func (p *CRIProvider) RunInContainer(ctx context.Context, namespace, name, container string, cmd []string, attach api.AttachIO) error {
log.Printf("receive ExecInContainer %q\n", container) log.Printf("receive ExecInContainer %q\n", container)
return nil return nil
} }

View File

@@ -17,8 +17,8 @@ import (
"github.com/cpuguy83/strongerrors" "github.com/cpuguy83/strongerrors"
"github.com/virtual-kubelet/virtual-kubelet/manager" "github.com/virtual-kubelet/virtual-kubelet/manager"
"github.com/virtual-kubelet/virtual-kubelet/providers"
"github.com/virtual-kubelet/virtual-kubelet/providers/huawei/auth" "github.com/virtual-kubelet/virtual-kubelet/providers/huawei/auth"
"github.com/virtual-kubelet/virtual-kubelet/vkubelet/api"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -299,7 +299,7 @@ func (p *CCIProvider) GetPod(ctx context.Context, namespace, name string) (*v1.P
} }
// GetContainerLogs retrieves the logs of a container by name from the huawei CCI provider. // GetContainerLogs retrieves the logs of a container by name from the huawei CCI provider.
func (p *CCIProvider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts providers.ContainerLogOpts) (io.ReadCloser, error) { func (p *CCIProvider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts api.ContainerLogOpts) (io.ReadCloser, error) {
return ioutil.NopCloser(strings.NewReader("")), nil return ioutil.NopCloser(strings.NewReader("")), nil
} }
@@ -312,7 +312,7 @@ func (p *CCIProvider) GetPodFullName(namespace string, pod string) string {
// RunInContainer executes a command in a container in the pod, copying data // RunInContainer executes a command in a container in the pod, copying data
// between in/out/err and the container's stdin/stdout/stderr. // between in/out/err and the container's stdin/stdout/stderr.
// TODO: Implementation // TODO: Implementation
func (p *CCIProvider) RunInContainer(ctx context.Context, namespace, name, container string, cmd []string, attach providers.AttachIO) error { func (p *CCIProvider) RunInContainer(ctx context.Context, namespace, name, container string, cmd []string, attach api.AttachIO) error {
log.Printf("receive ExecInContainer %q\n", container) log.Printf("receive ExecInContainer %q\n", container)
return nil return nil
} }

View File

@@ -14,12 +14,11 @@ import (
"github.com/cpuguy83/strongerrors/status/ocstatus" "github.com/cpuguy83/strongerrors/status/ocstatus"
"github.com/virtual-kubelet/virtual-kubelet/log" "github.com/virtual-kubelet/virtual-kubelet/log"
"github.com/virtual-kubelet/virtual-kubelet/trace" "github.com/virtual-kubelet/virtual-kubelet/trace"
"github.com/virtual-kubelet/virtual-kubelet/vkubelet/api"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
stats "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1" stats "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
"github.com/virtual-kubelet/virtual-kubelet/providers"
) )
const ( const (
@@ -223,7 +222,7 @@ func (p *MockProvider) GetPod(ctx context.Context, namespace, name string) (pod
} }
// GetContainerLogs retrieves the logs of a container by name from the provider. // GetContainerLogs retrieves the logs of a container by name from the provider.
func (p *MockProvider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts providers.ContainerLogOpts) (io.ReadCloser, error) { func (p *MockProvider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts api.ContainerLogOpts) (io.ReadCloser, error) {
ctx, span := trace.StartSpan(ctx, "GetContainerLogs") ctx, span := trace.StartSpan(ctx, "GetContainerLogs")
defer span.End() defer span.End()
@@ -242,7 +241,7 @@ func (p *MockProvider) GetPodFullName(namespace string, pod string) string {
// RunInContainer executes a command in a container in the pod, copying data // RunInContainer executes a command in a container in the pod, copying data
// between in/out/err and the container's stdin/stdout/stderr. // between in/out/err and the container's stdin/stdout/stderr.
func (p *MockProvider) RunInContainer(ctx context.Context, namespace, name, container string, cmd []string, attach providers.AttachIO) error { func (p *MockProvider) RunInContainer(ctx context.Context, namespace, name, container string, cmd []string, attach api.AttachIO) error {
log.G(context.TODO()).Infof("receive ExecInContainer %q", container) log.G(context.TODO()).Infof("receive ExecInContainer %q", container)
return nil return nil
} }
@@ -413,7 +412,7 @@ func (p *MockProvider) NodeDaemonEndpoints(ctx context.Context) *v1.NodeDaemonEn
// OperatingSystem returns the operating system for this provider. // OperatingSystem returns the operating system for this provider.
// This is a noop to default to Linux for now. // This is a noop to default to Linux for now.
func (p *MockProvider) OperatingSystem() string { func (p *MockProvider) OperatingSystem() string {
return providers.OperatingSystemLinux return "Linux"
} }
// GetStatsSummary returns dummy stats for all pods known by this provider. // GetStatsSummary returns dummy stats for all pods known by this provider.

View File

@@ -1,28 +0,0 @@
package providers
import (
"context"
v1 "k8s.io/api/core/v1"
)
// NodeProvider is the interface used for registering a node and updating its
// status in Kubernetes.
//
// Note: Implementers can choose to manage a node themselves, in which case
// it is not needed to provide an implementation for this interface.
type NodeProvider interface {
// Ping checks if the node is still active.
// This is intended to be lightweight as it will be called periodically as a
// heartbeat to keep the node marked as ready in Kubernetes.
Ping(context.Context) error
// NotifyNodeStatus is used to asynchronously monitor the node.
// The passed in callback should be called any time there is a change to the
// node's status.
// This will generally trigger a call to the Kubernetes API server to update
// the status.
//
// NotifyNodeStatus should not block callers.
NotifyNodeStatus(ctx context.Context, cb func(*v1.Node))
}

View File

@@ -12,6 +12,7 @@ import (
nomad "github.com/hashicorp/nomad/api" nomad "github.com/hashicorp/nomad/api"
"github.com/virtual-kubelet/virtual-kubelet/manager" "github.com/virtual-kubelet/virtual-kubelet/manager"
"github.com/virtual-kubelet/virtual-kubelet/providers" "github.com/virtual-kubelet/virtual-kubelet/providers"
"github.com/virtual-kubelet/virtual-kubelet/vkubelet/api"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -147,7 +148,7 @@ func (p *Provider) GetPod(ctx context.Context, namespace, name string) (pod *v1.
} }
// GetContainerLogs retrieves the logs of a container by name from the provider. // GetContainerLogs retrieves the logs of a container by name from the provider.
func (p *Provider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts providers.ContainerLogOpts) (io.ReadCloser, error) { func (p *Provider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts api.ContainerLogOpts) (io.ReadCloser, error) {
return ioutil.NopCloser(strings.NewReader("")), nil return ioutil.NopCloser(strings.NewReader("")), nil
} }
@@ -159,7 +160,7 @@ func (p *Provider) GetPodFullName(ctx context.Context, namespace string, pod str
// RunInContainer executes a command in a container in the pod, copying data // RunInContainer executes a command in a container in the pod, copying data
// between in/out/err and the container's stdin/stdout/stderr. // between in/out/err and the container's stdin/stdout/stderr.
// TODO: Implementation // TODO: Implementation
func (p *Provider) RunInContainer(ctx context.Context, namespace, name, container string, cmd []string, attach providers.AttachIO) error { func (p *Provider) RunInContainer(ctx context.Context, namespace, name, container string, cmd []string, attach api.AttachIO) error {
log.Printf("ExecInContainer %q\n", container) log.Printf("ExecInContainer %q\n", container)
return nil return nil
} }

View File

@@ -18,6 +18,7 @@ import (
"github.com/gophercloud/gophercloud/pagination" "github.com/gophercloud/gophercloud/pagination"
"github.com/virtual-kubelet/virtual-kubelet/manager" "github.com/virtual-kubelet/virtual-kubelet/manager"
"github.com/virtual-kubelet/virtual-kubelet/providers" "github.com/virtual-kubelet/virtual-kubelet/providers"
"github.com/virtual-kubelet/virtual-kubelet/vkubelet/api"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -215,7 +216,7 @@ func (p *ZunProvider) getContainers(ctx context.Context, pod *v1.Pod) ([]Contain
// RunInContainer executes a command in a container in the pod, copying data // RunInContainer executes a command in a container in the pod, copying data
// between in/out/err and the container's stdin/stdout/stderr. // between in/out/err and the container's stdin/stdout/stderr.
func (p *ZunProvider) RunInContainer(ctx context.Context, namespace, name, container string, cmd []string, attach providers.AttachIO) error { func (p *ZunProvider) RunInContainer(ctx context.Context, namespace, name, container string, cmd []string, attach api.AttachIO) error {
log.Printf("receive ExecInContainer %q\n", container) log.Printf("receive ExecInContainer %q\n", container)
return nil return nil
} }
@@ -235,7 +236,7 @@ func (p *ZunProvider) GetPodStatus(ctx context.Context, namespace, name string)
return &pod.Status, nil return &pod.Status, nil
} }
func (p *ZunProvider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts providers.ContainerLogOpts) (io.ReadCloser, error) { func (p *ZunProvider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts api.ContainerLogOpts) (io.ReadCloser, error) {
return ioutil.NopCloser(strings.NewReader("not support in Zun Provider")), nil return ioutil.NopCloser(strings.NewReader("not support in Zun Provider")), nil
} }

View File

@@ -3,38 +3,23 @@ package providers
import ( import (
"context" "context"
"io" "io"
"time"
"github.com/virtual-kubelet/virtual-kubelet/vkubelet"
"github.com/virtual-kubelet/virtual-kubelet/vkubelet/api"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
stats "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1" stats "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
) )
// Provider contains the methods required to implement a virtual-kubelet provider. // Provider contains the methods required to implement a virtual-kubelet provider.
type Provider interface { type Provider interface {
// CreatePod takes a Kubernetes Pod and deploys it within the provider. vkubelet.PodLifecycleHandler
CreatePod(ctx context.Context, pod *v1.Pod) error
// UpdatePod takes a Kubernetes Pod and updates it within the provider.
UpdatePod(ctx context.Context, pod *v1.Pod) error
// DeletePod takes a Kubernetes Pod and deletes it from the provider.
DeletePod(ctx context.Context, pod *v1.Pod) error
// GetPod retrieves a pod by name from the provider (can be cached).
GetPod(ctx context.Context, namespace, name string) (*v1.Pod, error)
// GetContainerLogs retrieves the logs of a container by name from the provider. // GetContainerLogs retrieves the logs of a container by name from the provider.
GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts ContainerLogOpts) (io.ReadCloser, error) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts api.ContainerLogOpts) (io.ReadCloser, error)
// RunInContainer executes a command in a container in the pod, copying data // RunInContainer executes a command in a container in the pod, copying data
// between in/out/err and the container's stdin/stdout/stderr. // between in/out/err and the container's stdin/stdout/stderr.
RunInContainer(ctx context.Context, namespace, podName, containerName string, cmd []string, attach AttachIO) error RunInContainer(ctx context.Context, namespace, podName, containerName string, cmd []string, attach api.AttachIO) error
// GetPodStatus retrieves the status of a pod by name from the provider.
GetPodStatus(ctx context.Context, namespace, name string) (*v1.PodStatus, error)
// GetPods retrieves a list of all pods running on the provider (can be cached).
GetPods(context.Context) ([]*v1.Pod, error)
// Capacity returns a resource list with the capacity constraints of the provider. // Capacity returns a resource list with the capacity constraints of the provider.
Capacity(context.Context) v1.ResourceList Capacity(context.Context) v1.ResourceList
@@ -55,42 +40,7 @@ type Provider interface {
OperatingSystem() string OperatingSystem() string
} }
// ContainerLogOpts are used to pass along options to be set on the container
// log stream.
type ContainerLogOpts struct {
Tail int
Since time.Duration
LimitBytes int
Timestamps bool
}
// PodMetricsProvider is an optional interface that providers can implement to expose pod stats // PodMetricsProvider is an optional interface that providers can implement to expose pod stats
type PodMetricsProvider interface { type PodMetricsProvider interface {
GetStatsSummary(context.Context) (*stats.Summary, error) GetStatsSummary(context.Context) (*stats.Summary, error)
} }
// PodNotifier notifies callers of pod changes.
// Providers should implement this interface to enable callers to be notified
// of pod status updates asyncronously.
type PodNotifier interface {
// NotifyPods instructs the notifier to call the passed in function when
// the pod status changes.
//
// NotifyPods should not block callers.
NotifyPods(context.Context, func(*v1.Pod))
}
// AttachIO is used to pass in streams to attach to a container process
type AttachIO interface {
Stdin() io.Reader
Stdout() io.WriteCloser
Stderr() io.WriteCloser
TTY() bool
Resize() <-chan TermSize
}
// TermSize is used to set the terminal size from attached clients.
type TermSize struct {
Width uint16
Height uint16
}

View File

@@ -30,7 +30,7 @@ import (
"github.com/cenkalti/backoff" "github.com/cenkalti/backoff"
"github.com/cpuguy83/strongerrors" "github.com/cpuguy83/strongerrors"
"github.com/virtual-kubelet/virtual-kubelet/providers" "github.com/virtual-kubelet/virtual-kubelet/vkubelet/api"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
) )
@@ -109,7 +109,7 @@ func (p *BrokerProvider) GetPod(ctx context.Context, namespace, name string) (*v
} }
// GetContainerLogs returns the logs of a container running in a pod by name. // GetContainerLogs returns the logs of a container running in a pod by name.
func (p *BrokerProvider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts providers.ContainerLogOpts) (io.ReadCloser, error) { func (p *BrokerProvider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts api.ContainerLogOpts) (io.ReadCloser, error) {
urlPathStr := fmt.Sprintf( urlPathStr := fmt.Sprintf(
"/getContainerLogs?namespace=%s&podName=%s&containerName=%s&tail=%d", "/getContainerLogs?namespace=%s&podName=%s&containerName=%s&tail=%d",
url.QueryEscape(namespace), url.QueryEscape(namespace),
@@ -134,7 +134,7 @@ func (p *BrokerProvider) GetPodFullName(namespace string, pod string) string {
// RunInContainer executes a command in a container in the pod, copying data // RunInContainer executes a command in a container in the pod, copying data
// between in/out/err and the container's stdin/stdout/stderr. // between in/out/err and the container's stdin/stdout/stderr.
// TODO: Implementation // TODO: Implementation
func (p *BrokerProvider) RunInContainer(ctx context.Context, namespace, name, container string, cmd []string, attach providers.AttachIO) error { func (p *BrokerProvider) RunInContainer(ctx context.Context, namespace, name, container string, cmd []string, attach api.AttachIO) error {
log.Printf("receive ExecInContainer %q\n", container) log.Printf("receive ExecInContainer %q\n", container)
return nil return nil
} }

View File

@@ -10,21 +10,38 @@ import (
"github.com/cpuguy83/strongerrors" "github.com/cpuguy83/strongerrors"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/virtual-kubelet/virtual-kubelet/providers"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
remoteutils "k8s.io/client-go/tools/remotecommand" remoteutils "k8s.io/client-go/tools/remotecommand"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/kubelet/server/remotecommand" "k8s.io/kubernetes/pkg/kubelet/server/remotecommand"
) )
type ExecBackend interface { // ContainerExecHandlerFunc defines the handler function used for "execing" into a
RunInContainer(ctx context.Context, namespace, podName, containerName string, cmd []string, attach providers.AttachIO) error // container in a pod.
type ContainerExecHandlerFunc func(ctx context.Context, namespace, podName, containerName string, cmd []string, attach AttachIO) error
// AttachIO is used to pass in streams to attach to a container process
type AttachIO interface {
Stdin() io.Reader
Stdout() io.WriteCloser
Stderr() io.WriteCloser
TTY() bool
Resize() <-chan TermSize
} }
// PodExecHandlerFunc makes an http handler func from a Provider which execs a command in a pod's container // TermSize is used to set the terminal size from attached clients.
type TermSize struct {
Width uint16
Height uint16
}
// HandleContainerExec makes an http handler func from a Provider which execs a command in a pod's container
// Note that this handler currently depends on gorrilla/mux to get url parts as variables. // Note that this handler currently depends on gorrilla/mux to get url parts as variables.
// TODO(@cpuguy83): don't force gorilla/mux on consumers of this function // TODO(@cpuguy83): don't force gorilla/mux on consumers of this function
func PodExecHandlerFunc(backend ExecBackend) http.HandlerFunc { func HandleContainerExec(h ContainerExecHandlerFunc) http.HandlerFunc {
if h == nil {
return NotImplemented
}
return handleError(func(w http.ResponseWriter, req *http.Request) error { return handleError(func(w http.ResponseWriter, req *http.Request) error {
vars := mux.Vars(req) vars := mux.Vars(req)
@@ -48,7 +65,7 @@ func PodExecHandlerFunc(backend ExecBackend) http.HandlerFunc {
ctx, cancel := context.WithCancel(context.TODO()) ctx, cancel := context.WithCancel(context.TODO())
defer cancel() defer cancel()
exec := &containerExecContext{ctx: ctx, b: backend, pod: pod, namespace: namespace, container: container} exec := &containerExecContext{ctx: ctx, h: h, pod: pod, namespace: namespace, container: container}
remotecommand.ServeExec(w, req, exec, "", "", container, command, streamOpts, idleTimeout, streamCreationTimeout, supportedStreamProtocols) remotecommand.ServeExec(w, req, exec, "", "", container, command, streamOpts, idleTimeout, streamCreationTimeout, supportedStreamProtocols)
return nil return nil
@@ -77,7 +94,7 @@ func getExecOptions(req *http.Request) (*remotecommand.Options, error) {
} }
type containerExecContext struct { type containerExecContext struct {
b ExecBackend h ContainerExecHandlerFunc
eio *execIO eio *execIO
namespace, pod, container string namespace, pod, container string
ctx context.Context ctx context.Context
@@ -95,7 +112,7 @@ func (c *containerExecContext) ExecInContainer(name string, uid types.UID, conta
} }
if tty { if tty {
eio.chResize = make(chan providers.TermSize) eio.chResize = make(chan TermSize)
} }
ctx, cancel := context.WithCancel(c.ctx) ctx, cancel := context.WithCancel(c.ctx)
@@ -105,7 +122,7 @@ func (c *containerExecContext) ExecInContainer(name string, uid types.UID, conta
go func() { go func() {
send := func(s remoteutils.TerminalSize) bool { send := func(s remoteutils.TerminalSize) bool {
select { select {
case eio.chResize <- providers.TermSize{Width: s.Width, Height: s.Height}: case eio.chResize <- TermSize{Width: s.Width, Height: s.Height}:
return false return false
case <-ctx.Done(): case <-ctx.Done():
return true return true
@@ -125,7 +142,7 @@ func (c *containerExecContext) ExecInContainer(name string, uid types.UID, conta
}() }()
} }
return c.b.RunInContainer(c.ctx, c.namespace, c.pod, c.container, cmd, eio) return c.h(c.ctx, c.namespace, c.pod, c.container, cmd, eio)
} }
type execIO struct { type execIO struct {
@@ -133,7 +150,7 @@ type execIO struct {
stdin io.Reader stdin io.Reader
stdout io.WriteCloser stdout io.WriteCloser
stderr io.WriteCloser stderr io.WriteCloser
chResize chan providers.TermSize chResize chan TermSize
} }
func (e *execIO) TTY() bool { func (e *execIO) TTY() bool {
@@ -152,6 +169,6 @@ func (e *execIO) Stderr() io.WriteCloser {
return e.stderr return e.stderr
} }
func (e *execIO) Resize() <-chan providers.TermSize { func (e *execIO) Resize() <-chan TermSize {
return e.chResize return e.chResize
} }

View File

@@ -5,21 +5,31 @@ import (
"io" "io"
"net/http" "net/http"
"strconv" "strconv"
"time"
"github.com/cpuguy83/strongerrors" "github.com/cpuguy83/strongerrors"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/virtual-kubelet/virtual-kubelet/log" "github.com/virtual-kubelet/virtual-kubelet/log"
"github.com/virtual-kubelet/virtual-kubelet/providers"
) )
// ContainerLogsBackend is used in place of backend implementations for getting container logs // ContainerLogsHandlerFunc is used in place of backend implementations for getting container logs
type ContainerLogsBackend interface { type ContainerLogsHandlerFunc func(ctx context.Context, namespace, podName, containerName string, opts ContainerLogOpts) (io.ReadCloser, error)
GetContainerLogs(ctx context.Context, namespace, podName, containerName string, opts providers.ContainerLogOpts) (io.ReadCloser, error)
// ContainerLogOpts are used to pass along options to be set on the container
// log stream.
type ContainerLogOpts struct {
Tail int
Since time.Duration
LimitBytes int
Timestamps bool
} }
// PodLogsHandlerFunc creates an http handler function from a provider to serve logs from a pod // HandleContainerLogs creates an http handler function from a provider to serve logs from a pod
func PodLogsHandlerFunc(p ContainerLogsBackend) http.HandlerFunc { func HandleContainerLogs(h ContainerLogsHandlerFunc) http.HandlerFunc {
if h == nil {
return NotImplemented
}
return handleError(func(w http.ResponseWriter, req *http.Request) error { return handleError(func(w http.ResponseWriter, req *http.Request) error {
vars := mux.Vars(req) vars := mux.Vars(req)
if len(vars) != 3 { if len(vars) != 3 {
@@ -45,11 +55,11 @@ func PodLogsHandlerFunc(p ContainerLogsBackend) http.HandlerFunc {
// TODO(@cpuguy83): support v1.PodLogOptions // TODO(@cpuguy83): support v1.PodLogOptions
// The kubelet decoding here is not straight forward, so this needs to be disected // The kubelet decoding here is not straight forward, so this needs to be disected
opts := providers.ContainerLogOpts{ opts := ContainerLogOpts{
Tail: tail, Tail: tail,
} }
logs, err := p.GetContainerLogs(ctx, namespace, pod, container, opts) logs, err := h(ctx, namespace, pod, container, opts)
if err != nil { if err != nil {
return errors.Wrap(err, "error getting container logs?)") return errors.Wrap(err, "error getting container logs?)")
} }

View File

@@ -6,18 +6,14 @@ import (
"github.com/cpuguy83/strongerrors" "github.com/cpuguy83/strongerrors"
"github.com/virtual-kubelet/virtual-kubelet/log" "github.com/virtual-kubelet/virtual-kubelet/log"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/runtime/serializer"
) )
// ContainerLogsBackend is used in place of backend implementations for the provider's pods type PodListerFunc func(context.Context) ([]*v1.Pod, error)
type RunningPodsBackend interface {
// GetPods retrieves a list of all pods running on the provider (can be cached).
GetPods(context.Context) ([]*v1.Pod, error)
}
func RunningPodsHandlerFunc(p RunningPodsBackend) http.HandlerFunc { func HandleRunningPods(getPods PodListerFunc) http.HandlerFunc {
scheme := runtime.NewScheme() scheme := runtime.NewScheme()
v1.SchemeBuilder.AddToScheme(scheme) v1.SchemeBuilder.AddToScheme(scheme)
codecs := serializer.NewCodecFactory(scheme) codecs := serializer.NewCodecFactory(scheme)
@@ -25,7 +21,7 @@ func RunningPodsHandlerFunc(p RunningPodsBackend) http.HandlerFunc {
return handleError(func(w http.ResponseWriter, req *http.Request) error { return handleError(func(w http.ResponseWriter, req *http.Request) error {
ctx := req.Context() ctx := req.Context()
ctx = log.WithLogger(ctx, log.L) ctx = log.WithLogger(ctx, log.L)
pods, err := p.GetPods(ctx) pods, err := getPods(ctx)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,12 +1,10 @@
package vkubelet package api
import ( import (
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/virtual-kubelet/virtual-kubelet/log" "github.com/virtual-kubelet/virtual-kubelet/log"
"github.com/virtual-kubelet/virtual-kubelet/providers"
"github.com/virtual-kubelet/virtual-kubelet/vkubelet/api"
"go.opencensus.io/plugin/ochttp" "go.opencensus.io/plugin/ochttp"
"go.opencensus.io/plugin/ochttp/propagation/b3" "go.opencensus.io/plugin/ochttp/propagation/b3"
) )
@@ -20,37 +18,40 @@ type ServeMux interface {
Handle(path string, h http.Handler) Handle(path string, h http.Handler)
} }
type PodHandlerConfig struct {
RunInContainer ContainerExecHandlerFunc
GetContainerLogs ContainerLogsHandlerFunc
GetPods PodListerFunc
}
// PodHandler creates an http handler for interacting with pods/containers. // PodHandler creates an http handler for interacting with pods/containers.
func PodHandler(p providers.Provider, debug bool) http.Handler { func PodHandler(p PodHandlerConfig, debug bool) http.Handler {
r := mux.NewRouter() r := mux.NewRouter()
// This matches the behaviour in the reference kubelet // This matches the behaviour in the reference kubelet
r.StrictSlash(true) r.StrictSlash(true)
if debug { if debug {
r.HandleFunc("/runningpods/", api.RunningPodsHandlerFunc(p)).Methods("GET") r.HandleFunc("/runningpods/", HandleRunningPods(p.GetPods)).Methods("GET")
} }
r.HandleFunc("/containerLogs/{namespace}/{pod}/{container}", api.PodLogsHandlerFunc(p)).Methods("GET") r.HandleFunc("/containerLogs/{namespace}/{pod}/{container}", HandleContainerLogs(p.GetContainerLogs)).Methods("GET")
r.HandleFunc("/exec/{namespace}/{pod}/{container}", api.PodExecHandlerFunc(p)).Methods("POST") r.HandleFunc("/exec/{namespace}/{pod}/{container}", HandleContainerExec(p.RunInContainer)).Methods("POST")
r.NotFoundHandler = http.HandlerFunc(NotFound) r.NotFoundHandler = http.HandlerFunc(NotFound)
return r return r
} }
// MetricsSummaryHandler creates an http handler for serving pod metrics. // PodStatsSummaryHandler creates an http handler for serving pod metrics.
// //
// If the passed in provider does not implement providers.PodMetricsProvider, // If the passed in handler func is nil this will create handlers which only
// it will create handlers that just serves http.StatusNotImplemented // serves http.StatusNotImplemented
func MetricsSummaryHandler(p providers.Provider) http.Handler { func PodStatsSummaryHandler(f PodStatsSummaryHandlerFunc) http.Handler {
if f == nil {
return http.HandlerFunc(NotImplemented)
}
r := mux.NewRouter() r := mux.NewRouter()
const summaryRoute = "/stats/summary" const summaryRoute = "/stats/summary"
var h http.HandlerFunc h := HandlePodStatsSummary(f)
mp, ok := p.(providers.PodMetricsProvider)
if !ok {
h = NotImplemented
} else {
h = api.PodMetricsHandlerFunc(mp)
}
r.Handle(summaryRoute, ochttp.WithRouteTag(h, "PodStatsSummaryHandler")).Methods("GET") r.Handle(summaryRoute, ochttp.WithRouteTag(h, "PodStatsSummaryHandler")).Methods("GET")
r.Handle(summaryRoute+"/", ochttp.WithRouteTag(h, "PodStatsSummaryHandler")).Methods("GET") r.Handle(summaryRoute+"/", ochttp.WithRouteTag(h, "PodStatsSummaryHandler")).Methods("GET")
@@ -63,16 +64,25 @@ func MetricsSummaryHandler(p providers.Provider) http.Handler {
// //
// Callers should take care to namespace the serve mux as they see fit, however // Callers should take care to namespace the serve mux as they see fit, however
// these routes get called by the Kubernetes API server. // these routes get called by the Kubernetes API server.
func AttachPodRoutes(p providers.Provider, mux ServeMux, debug bool) { func AttachPodRoutes(p PodHandlerConfig, mux ServeMux, debug bool) {
mux.Handle("/", InstrumentHandler(PodHandler(p, debug))) mux.Handle("/", InstrumentHandler(PodHandler(p, debug)))
} }
// AttachMetricsRoutes adds the http routes for pod/node metrics to the passed in serve mux. // PodMetricsConfig stores the handlers for pod metrics routes
// It is used by AttachPodMetrics.
//
// The main reason for this struct is in case of expansion we do not need to break
// the package level API.
type PodMetricsConfig struct {
GetStatsSummary PodStatsSummaryHandlerFunc
}
// AttachPodMetricsRoutes adds the http routes for pod/node metrics to the passed in serve mux.
// //
// Callers should take care to namespace the serve mux as they see fit, however // Callers should take care to namespace the serve mux as they see fit, however
// these routes get called by the Kubernetes API server. // these routes get called by the Kubernetes API server.
func AttachMetricsRoutes(p providers.Provider, mux ServeMux) { func AttachPodMetricsRoutes(p PodMetricsConfig, mux ServeMux) {
mux.Handle("/", InstrumentHandler(MetricsSummaryHandler(p))) mux.Handle("/", InstrumentHandler(HandlePodStatsSummary(p.GetStatsSummary)))
} }
func instrumentRequest(r *http.Request) *http.Request { func instrumentRequest(r *http.Request) *http.Request {

View File

@@ -10,15 +10,16 @@ import (
stats "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1" stats "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
) )
// PodMetricsBackend is used in place of backend implementations to get k8s pod metrics. // PodStatsSummaryHandlerFunc defines the handler for getting pod stats summaries
type PodMetricsBackend interface { type PodStatsSummaryHandlerFunc func(context.Context) (*stats.Summary, error)
GetStatsSummary(context.Context) (*stats.Summary, error)
}
// PodMetricsHandlerFunc makes an HTTP handler for implementing the kubelet summary stats endpoint // HandlePodStatsSummary makes an HTTP handler for implementing the kubelet summary stats endpoint
func PodMetricsHandlerFunc(b PodMetricsBackend) http.HandlerFunc { func HandlePodStatsSummary(h PodStatsSummaryHandlerFunc) http.HandlerFunc {
if h == nil {
return NotImplemented
}
return handleError(func(w http.ResponseWriter, req *http.Request) error { return handleError(func(w http.ResponseWriter, req *http.Request) error {
stats, err := b.GetStatsSummary(req.Context()) stats, err := h(req.Context())
if err != nil { if err != nil {
if errors.Cause(err) == context.Canceled { if errors.Cause(err) == context.Canceled {
return strongerrors.Cancelled(err) return strongerrors.Cancelled(err)

View File

@@ -19,11 +19,11 @@ controller.
Up to this point you have a running virtual kubelet controller, but no HTTP Up to this point you have a running virtual kubelet controller, but no HTTP
handlers to deal with requests forwarded from the API server for things like handlers to deal with requests forwarded from the API server for things like
pod logs (e.g. user calls `kubectl logs myVKPod`). This package provides some pod logs (e.g. user calls `kubectl logs myVKPod`). The api package provides some
helpers for this: `AttachPodRoutes` and `AttachMetricsRoutes`. helpers for this: `api.AttachPodRoutes` and `api.AttachMetricsRoutes`.
mux := http.NewServeMux() mux := http.NewServeMux()
vkubelet.AttachPodRoutes(provider, mux) api.AttachPodRoutes(provider, mux)
You must configure your own HTTP server, but these helpers will add handlers at You must configure your own HTTP server, but these helpers will add handlers at
the correct URI paths to your serve mux. You are not required to use go's the correct URI paths to your serve mux. You are not required to use go's

View File

@@ -9,7 +9,6 @@ import (
"github.com/cpuguy83/strongerrors/status/ocstatus" "github.com/cpuguy83/strongerrors/status/ocstatus"
pkgerrors "github.com/pkg/errors" pkgerrors "github.com/pkg/errors"
"github.com/virtual-kubelet/virtual-kubelet/log" "github.com/virtual-kubelet/virtual-kubelet/log"
"github.com/virtual-kubelet/virtual-kubelet/providers"
"github.com/virtual-kubelet/virtual-kubelet/trace" "github.com/virtual-kubelet/virtual-kubelet/trace"
coord "k8s.io/api/coordination/v1beta1" coord "k8s.io/api/coordination/v1beta1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
@@ -21,12 +20,33 @@ import (
v1 "k8s.io/client-go/kubernetes/typed/core/v1" v1 "k8s.io/client-go/kubernetes/typed/core/v1"
) )
// NodeProvider is the interface used for registering a node and updating its
// status in Kubernetes.
//
// Note: Implementers can choose to manage a node themselves, in which case
// it is not needed to provide an implementation for this interface.
type NodeProvider interface {
// Ping checks if the node is still active.
// This is intended to be lightweight as it will be called periodically as a
// heartbeat to keep the node marked as ready in Kubernetes.
Ping(context.Context) error
// NotifyNodeStatus is used to asynchronously monitor the node.
// The passed in callback should be called any time there is a change to the
// node's status.
// This will generally trigger a call to the Kubernetes API server to update
// the status.
//
// NotifyNodeStatus should not block callers.
NotifyNodeStatus(ctx context.Context, cb func(*corev1.Node))
}
// NewNode creates a new node. // NewNode creates a new node.
// This does not have any side-effects on the system or kubernetes. // This does not have any side-effects on the system or kubernetes.
// //
// Use the node's `Run` method to register and run the loops to update the node // Use the node's `Run` method to register and run the loops to update the node
// in Kubernetes. // in Kubernetes.
func NewNode(p providers.NodeProvider, node *corev1.Node, leases v1beta1.LeaseInterface, nodes v1.NodeInterface, opts ...NodeOpt) (*Node, error) { func NewNode(p NodeProvider, node *corev1.Node, leases v1beta1.LeaseInterface, nodes v1.NodeInterface, opts ...NodeOpt) (*Node, error) {
n := &Node{p: p, n: node, leases: leases, nodes: nodes} n := &Node{p: p, n: node, leases: leases, nodes: nodes}
for _, o := range opts { for _, o := range opts {
if err := o(n); err != nil { if err := o(n); err != nil {
@@ -79,7 +99,7 @@ func WithNodeLease(l *coord.Lease) NodeOpt {
// Node deals with creating and managing a node object in Kubernetes. // Node deals with creating and managing a node object in Kubernetes.
// It can register a node with Kubernetes and periodically update its status. // It can register a node with Kubernetes and periodically update its status.
type Node struct { type Node struct {
p providers.NodeProvider p NodeProvider
n *corev1.Node n *corev1.Node
leases v1beta1.LeaseInterface leases v1beta1.LeaseInterface

View File

@@ -6,7 +6,6 @@ import (
"testing" "testing"
"time" "time"
"github.com/virtual-kubelet/virtual-kubelet/providers"
"gotest.tools/assert" "gotest.tools/assert"
"gotest.tools/assert/cmp" "gotest.tools/assert/cmp"
coord "k8s.io/api/coordination/v1beta1" coord "k8s.io/api/coordination/v1beta1"
@@ -232,7 +231,7 @@ func testNode(t *testing.T) *corev1.Node {
} }
type testNodeProvider struct { type testNodeProvider struct {
providers.NodeProvider NodeProvider
statusHandlers []func(*corev1.Node) statusHandlers []func(*corev1.Node)
} }

View File

@@ -39,6 +39,39 @@ import (
"github.com/virtual-kubelet/virtual-kubelet/log" "github.com/virtual-kubelet/virtual-kubelet/log"
) )
// PodLifecycleHandler defines the interface used by the PodController to react
// to new and changed pods scheduled to the node that is being managed.
type PodLifecycleHandler interface {
// CreatePod takes a Kubernetes Pod and deploys it within the provider.
CreatePod(ctx context.Context, pod *corev1.Pod) error
// UpdatePod takes a Kubernetes Pod and updates it within the provider.
UpdatePod(ctx context.Context, pod *corev1.Pod) error
// DeletePod takes a Kubernetes Pod and deletes it from the provider.
DeletePod(ctx context.Context, pod *corev1.Pod) error
// GetPod retrieves a pod by name from the provider (can be cached).
GetPod(ctx context.Context, namespace, name string) (*corev1.Pod, error)
// GetPodStatus retrieves the status of a pod by name from the provider.
GetPodStatus(ctx context.Context, namespace, name string) (*corev1.PodStatus, error)
// GetPods retrieves a list of all pods running on the provider (can be cached).
GetPods(context.Context) ([]*corev1.Pod, error)
}
// PodNotifier notifies callers of pod changes.
// Providers should implement this interface to enable callers to be notified
// of pod status updates asyncronously.
type PodNotifier interface {
// NotifyPods instructs the notifier to call the passed in function when
// the pod status changes.
//
// NotifyPods should not block callers.
NotifyPods(context.Context, func(*corev1.Pod))
}
// PodController is the controller implementation for Pod resources. // PodController is the controller implementation for Pod resources.
type PodController struct { type PodController struct {
// server is the instance to which this controller belongs. // server is the instance to which this controller belongs.

View File

@@ -6,7 +6,6 @@ import (
"time" "time"
"github.com/virtual-kubelet/virtual-kubelet/manager" "github.com/virtual-kubelet/virtual-kubelet/manager"
"github.com/virtual-kubelet/virtual-kubelet/providers"
"github.com/virtual-kubelet/virtual-kubelet/trace" "github.com/virtual-kubelet/virtual-kubelet/trace"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
corev1informers "k8s.io/client-go/informers/core/v1" corev1informers "k8s.io/client-go/informers/core/v1"
@@ -23,7 +22,7 @@ type Server struct {
namespace string namespace string
nodeName string nodeName string
k8sClient kubernetes.Interface k8sClient kubernetes.Interface
provider providers.Provider provider PodLifecycleHandler
resourceManager *manager.ResourceManager resourceManager *manager.ResourceManager
podSyncWorkers int podSyncWorkers int
podInformer corev1informers.PodInformer podInformer corev1informers.PodInformer
@@ -35,7 +34,7 @@ type Config struct {
Client *kubernetes.Clientset Client *kubernetes.Clientset
Namespace string Namespace string
NodeName string NodeName string
Provider providers.Provider Provider PodLifecycleHandler
ResourceManager *manager.ResourceManager ResourceManager *manager.ResourceManager
PodSyncWorkers int PodSyncWorkers int
PodInformer corev1informers.PodInformer PodInformer corev1informers.PodInformer
@@ -68,7 +67,7 @@ func (s *Server) Run(ctx context.Context) error {
q := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "podStatusUpdate") q := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "podStatusUpdate")
s.runProviderSyncWorkers(ctx, q) s.runProviderSyncWorkers(ctx, q)
if pn, ok := s.provider.(providers.PodNotifier); ok { if pn, ok := s.provider.(PodNotifier); ok {
pn.NotifyPods(ctx, func(pod *corev1.Pod) { pn.NotifyPods(ctx, func(pod *corev1.Pod) {
s.enqueuePodStatusUpdate(ctx, q, pod) s.enqueuePodStatusUpdate(ctx, q, pod)
}) })