Files
virtual-kubelet/providers/sfmesh/sfmesh.go
2018-08-01 16:25:47 -07:00

852 lines
24 KiB
Go

package sfmesh
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"os"
"strconv"
"time"
"github.com/Azure/azure-sdk-for-go/profiles/preview/preview/servicefabricmesh/mgmt/servicefabricmesh"
"github.com/Azure/go-autorest/autorest/azure/auth"
"github.com/virtual-kubelet/virtual-kubelet/manager"
"github.com/virtual-kubelet/virtual-kubelet/providers"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/remotecommand"
)
const (
defaultCPUCapacity = "60"
defaultMemoryCapacity = "48Gi"
defaultPodCapacity = "5"
defaultCPURequests = 1.0
defaultMemoryRequests = 1.0
defaultCPULimit = 4.0
defaultMemoryLimit = 16.0
)
// SFMeshProvider implements the Virtual Kubelet provider interface
type SFMeshProvider struct {
nodeName string
operatingSystem string
internalIP string
daemonEndpointPort int32
appClient *servicefabricmesh.ApplicationClient
networkClient *servicefabricmesh.NetworkClient
serviceClient *servicefabricmesh.ServiceClient
region string
resourceGroup string
subscriptionID string
resourceManager *manager.ResourceManager
}
// AuthConfig is the secret returned from an ImageRegistryCredential
type AuthConfig struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
Auth string `json:"auth,omitempty"`
Email string `json:"email,omitempty"`
ServerAddress string `json:"serveraddress,omitempty"`
IdentityToken string `json:"identitytoken,omitempty"`
RegistryToken string `json:"registrytoken,omitempty"`
}
// NewSFMeshProvider creates a new SFMeshProvider
func NewSFMeshProvider(rm *manager.ResourceManager, nodeName, operatingSystem string, internalIP string, daemonEndpointPort int32) (*SFMeshProvider, error) {
azureSubscriptionID := os.Getenv("AZURE_SUBSCRIPTION_ID")
azureTenantID := os.Getenv("AZURE_TENANT_ID")
azureClientID := os.Getenv("AZURE_CLIENT_ID")
azureClientSecret := os.Getenv("AZURE_CLIENT_SECRET")
region := os.Getenv("REGION")
resourceGroup := os.Getenv("RESOURCE_GROUP")
if azureSubscriptionID == "" {
return nil, errors.New("Subscription ID cannot be empty, please set AZURE_SUBSCRIPTION_ID")
}
if azureTenantID == "" {
return nil, errors.New("Tenant ID cannot be empty, please set AZURE_TENANT_ID")
}
if azureClientID == "" {
return nil, errors.New("Client ID cannot be empty, please set AZURE_CLIENT_ID ")
}
if azureClientSecret == "" {
return nil, errors.New("Client Secret cannot be empty, please set AZURE_CLIENT_SECRET ")
}
if region == "" {
return nil, errors.New("Region cannot be empty, please set REGION ")
}
if resourceGroup == "" {
return nil, errors.New("Resource Group cannot be empty, please set RESOURCE_GROUP ")
}
client := servicefabricmesh.NewApplicationClient(azureSubscriptionID)
auth, err := auth.NewAuthorizerFromEnvironment()
if err != nil {
return nil, err
}
client.Authorizer = auth
networkClient := servicefabricmesh.NewNetworkClient(azureSubscriptionID)
networkClient.Authorizer = auth
serviceClient := servicefabricmesh.NewServiceClient(azureSubscriptionID)
serviceClient.Authorizer = auth
provider := SFMeshProvider{
nodeName: nodeName,
operatingSystem: operatingSystem,
internalIP: internalIP,
daemonEndpointPort: daemonEndpointPort,
appClient: &client,
networkClient: &networkClient,
serviceClient: &serviceClient,
region: region,
resourceGroup: resourceGroup,
resourceManager: rm,
subscriptionID: azureSubscriptionID,
}
return &provider, nil
}
func readDockerCfgSecret(secret *v1.Secret, ips []servicefabricmesh.ImageRegistryCredential) ([]servicefabricmesh.ImageRegistryCredential, error) {
var err error
var authConfigs map[string]AuthConfig
repoData, ok := secret.Data[string(v1.DockerConfigKey)]
if !ok {
return ips, fmt.Errorf("no dockercfg present in secret")
}
err = json.Unmarshal(repoData, &authConfigs)
if err != nil {
return ips, err
}
for server, authConfig := range authConfigs {
ips = append(ips, servicefabricmesh.ImageRegistryCredential{
Password: &authConfig.Password,
Server: &server,
Username: &authConfig.Username,
})
}
return ips, err
}
func readDockerConfigJSONSecret(secret *v1.Secret, ips []servicefabricmesh.ImageRegistryCredential) ([]servicefabricmesh.ImageRegistryCredential, error) {
var err error
repoData, ok := secret.Data[string(v1.DockerConfigJsonKey)]
if !ok {
return ips, fmt.Errorf("no dockerconfigjson present in secret")
}
var authConfigs map[string]map[string]AuthConfig
err = json.Unmarshal(repoData, &authConfigs)
if err != nil {
return ips, err
}
auths, ok := authConfigs["auths"]
if !ok {
return ips, fmt.Errorf("malformed dockerconfigjson in secret")
}
for server, authConfig := range auths {
ips = append(ips, servicefabricmesh.ImageRegistryCredential{
Password: &authConfig.Password,
Server: &server,
Username: &authConfig.Username,
})
}
return ips, err
}
func (p *SFMeshProvider) getImagePullSecrets(pod *v1.Pod) ([]servicefabricmesh.ImageRegistryCredential, error) {
ips := make([]servicefabricmesh.ImageRegistryCredential, 0, len(pod.Spec.ImagePullSecrets))
for _, ref := range pod.Spec.ImagePullSecrets {
secret, err := p.resourceManager.GetSecret(ref.Name, pod.Namespace)
if err != nil {
return ips, err
}
if secret == nil {
return nil, fmt.Errorf("error getting image pull secret")
}
switch secret.Type {
case v1.SecretTypeDockercfg:
ips, err = readDockerCfgSecret(secret, ips)
case v1.SecretTypeDockerConfigJson:
ips, err = readDockerConfigJSONSecret(secret, ips)
default:
return nil, fmt.Errorf("image pull secret type is not one of kubernetes.io/dockercfg or kubernetes.io/dockerconfigjson")
}
if err != nil {
return ips, err
}
}
return ips, nil
}
func (p *SFMeshProvider) getMeshApplication(pod *v1.Pod) (servicefabricmesh.ApplicationResourceDescription, error) {
meshApp := servicefabricmesh.ApplicationResourceDescription{}
meshApp.Name = &pod.Name
meshApp.Location = &p.region
podUID := string(pod.UID)
podCreationTimestamp := pod.CreationTimestamp.String()
tags := map[string]*string{
"PodName": &pod.Name,
"ClusterName": &pod.ClusterName,
"NodeName": &pod.Spec.NodeName,
"Namespace": &pod.Namespace,
"UID": &podUID,
"CreationTimestamp": &podCreationTimestamp,
}
meshApp.Tags = tags
properties := servicefabricmesh.ApplicationResourceProperties{}
meshApp.ApplicationResourceProperties = &properties
services := []servicefabricmesh.ServiceResourceDescription{}
service := servicefabricmesh.ServiceResourceDescription{}
serviceName := *meshApp.Name + "-service"
service.Name = &serviceName
serviceType := "Microsoft.ServiceFabricMesh/services"
service.Type = &serviceType
creds, err := p.getImagePullSecrets(pod)
if err != nil {
return meshApp, err
}
codePackages := []servicefabricmesh.ContainerCodePackageProperties{}
for _, container := range pod.Spec.Containers {
codePackage := servicefabricmesh.ContainerCodePackageProperties{}
codePackage.Image = &container.Image
codePackage.Name = &container.Name
if creds != nil {
if len(creds) > 0 {
// Mesh ImageRegistryCredential supports only a single credential
codePackage.ImageRegistryCredential = &creds[0]
}
}
requirements := servicefabricmesh.ResourceRequirements{}
requests := servicefabricmesh.ResourceRequests{}
cpuRequest := defaultCPURequests
memoryRequest := defaultMemoryRequests
if container.Resources.Requests != nil {
if _, ok := container.Resources.Requests[v1.ResourceCPU]; ok {
containerCPURequest := float64(container.Resources.Requests.Cpu().MilliValue()/10.00) / 100.00
if containerCPURequest > 1 && containerCPURequest <= 4 {
cpuRequest = containerCPURequest
}
}
if _, ok := container.Resources.Requests[v1.ResourceMemory]; ok {
containerMemoryRequest := float64(container.Resources.Requests.Memory().Value()/100000000.00) / 10.00
if containerMemoryRequest < 0.10 {
containerMemoryRequest = 0.10
}
memoryRequest = containerMemoryRequest
}
}
requests.CPU = &cpuRequest
requests.MemoryInGB = &memoryRequest
requirements.Requests = &requests
if container.Resources.Limits != nil {
cpuLimit := defaultCPULimit
memoryLimit := defaultMemoryLimit
limits := servicefabricmesh.ResourceLimits{}
limits.CPU = &cpuLimit
limits.MemoryInGB = &memoryLimit
if _, ok := container.Resources.Limits[v1.ResourceCPU]; ok {
containerCPULimit := float64(container.Resources.Limits.Cpu().MilliValue()) / 1000.00
if containerCPULimit > 1 {
limits.CPU = &containerCPULimit
}
}
if _, ok := container.Resources.Limits[v1.ResourceMemory]; ok {
containerMemoryLimit := float64(container.Resources.Limits.Memory().Value()) / 1000000000.00
if containerMemoryLimit < 0.10 {
containerMemoryLimit = 0.10
}
limits.MemoryInGB = &containerMemoryLimit
}
requirements.Limits = &limits
}
codePackage.Resources = &requirements
if len(container.Command) > 0 {
codePackage.Commands = &container.Command
}
if len(container.Env) > 0 {
envVars := []servicefabricmesh.EnvironmentVariable{}
for _, envVar := range container.Env {
env := servicefabricmesh.EnvironmentVariable{}
env.Name = &envVar.Name
env.Value = &envVar.Value
envVars = append(envVars, env)
}
codePackage.EnvironmentVariables = &envVars
}
endpoints := []servicefabricmesh.EndpointProperties{}
for _, port := range container.Ports {
endpoint := p.getEndpointFromContainerPort(port)
endpoints = append(endpoints, endpoint)
}
if len(endpoints) > 0 {
codePackage.Endpoints = &endpoints
}
codePackages = append(codePackages, codePackage)
}
serviceProperties := servicefabricmesh.ServiceResourceProperties{}
serviceProperties.OsType = servicefabricmesh.Linux
replicaCount := int32(1)
serviceProperties.ReplicaCount = &replicaCount
serviceProperties.CodePackages = &codePackages
service.ServiceResourceProperties = &serviceProperties
services = append(services, service)
properties.Services = &services
return meshApp, nil
}
func (p *SFMeshProvider) getMeshNetwork(pod *v1.Pod, meshApp servicefabricmesh.ApplicationResourceDescription, location string) servicefabricmesh.NetworkResourceDescription {
network := servicefabricmesh.NetworkResourceDescription{}
network.Name = meshApp.Name
network.Location = &location
networkProperties := servicefabricmesh.NetworkResourceProperties{}
addressPrefix := "10.0.0.4/22"
networkProperties.AddressPrefix = &addressPrefix
layers := []servicefabricmesh.Layer4IngressConfig{}
service := (*meshApp.Services)[0]
for _, codePackage := range *service.CodePackages {
for _, endpoint := range *codePackage.Endpoints {
layer := p.getLayer(&endpoint, *meshApp.Name, *service.Name)
layers = append(layers, layer)
}
}
ingressConfig := servicefabricmesh.IngressConfig{}
ingressConfig.Layer4 = &layers
networkProperties.IngressConfig = &ingressConfig
network.NetworkResourceProperties = &networkProperties
return network
}
func (p *SFMeshProvider) getLayer(endpoint *servicefabricmesh.EndpointProperties, appName string, serviceName string) servicefabricmesh.Layer4IngressConfig {
layer := servicefabricmesh.Layer4IngressConfig{}
name := *endpoint.Name + "Ingress"
layerName := &name
layer.Name = layerName
layer.PublicPort = endpoint.Port
layer.EndpointName = endpoint.Name
layer.ApplicationName = &appName
layer.ServiceName = &serviceName
return layer
}
func (p *SFMeshProvider) getEndpointFromContainerPort(port v1.ContainerPort) servicefabricmesh.EndpointProperties {
endpoint := servicefabricmesh.EndpointProperties{}
endpointName := strconv.Itoa(int(port.ContainerPort)) + "Listener"
endpoint.Name = &endpointName
endpoint.Port = &port.ContainerPort
return endpoint
}
// CreatePod accepts a Pod definition and creates a SF Mesh App.
func (p *SFMeshProvider) CreatePod(pod *v1.Pod) error {
log.Printf("receive CreatePod %q\n", pod.Name)
meshApp, err := p.getMeshApplication(pod)
if err != nil {
return err
}
meshNetwork := p.getMeshNetwork(pod, meshApp, p.region)
_, err = p.networkClient.Create(context.Background(), p.resourceGroup, *meshNetwork.Name, meshNetwork)
if err != nil {
return err
}
networkName := *meshNetwork.Name
resourceID := "/subscriptions/" + p.subscriptionID + "/resourceGroups/" + p.resourceGroup + "/providers/Microsoft.ServiceFabricMesh/networks/" + networkName
service := (*meshApp.Services)[0]
networkRef := servicefabricmesh.NetworkRef{}
networkRef.Name = &resourceID
networkRefs := []servicefabricmesh.NetworkRef{}
networkRefs = append(networkRefs, networkRef)
service.NetworkRefs = &networkRefs
_, err = p.appClient.Create(context.Background(), p.resourceGroup, pod.Name, meshApp)
if err != nil {
return err
}
return nil
}
// UpdatePod updates the pod running inside SF Mesh.
func (p *SFMeshProvider) UpdatePod(pod *v1.Pod) error {
log.Printf("receive UpdatePod %q\n", pod.Name)
app, err := p.getMeshApplication(pod)
if err != nil {
return err
}
_, err = p.appClient.Create(context.Background(), p.resourceGroup, pod.Name, app)
if err != nil {
return err
}
return nil
}
// DeletePod deletes the specified pod out of SF Mesh.
func (p *SFMeshProvider) DeletePod(pod *v1.Pod) (err error) {
log.Printf("receive DeletePod %q\n", pod.Name)
_, err = p.appClient.Delete(context.Background(), p.resourceGroup, pod.Name)
if err != nil {
return err
}
return nil
}
// GetPod returns a pod by name that is running inside SF Mesh.
// returns nil if a pod by that name is not found.
func (p *SFMeshProvider) GetPod(namespace, name string) (pod *v1.Pod, err error) {
log.Printf("receive GetPod %q\n", name)
resp, err := p.appClient.Get(context.Background(), p.resourceGroup, name)
httpResponse := resp.Response.Response
if err != nil {
if httpResponse.StatusCode == 404 {
return nil, nil
}
return nil, err
}
if resp.Tags == nil {
return nil, nil
}
val, present := resp.Tags["NodeName"]
if !present {
return nil, nil
}
if *val != p.nodeName {
return nil, nil
}
pod, err = p.applicationDescriptionToPod(resp)
if err != nil {
return nil, err
}
return pod, nil
}
func (p *SFMeshProvider) appStateToPodPhase(state string) v1.PodPhase {
switch state {
case "Succeeded":
return v1.PodRunning
case "Failed":
return v1.PodFailed
case "Canceled":
return v1.PodFailed
case "Creating":
return v1.PodPending
case "Updating":
return v1.PodPending
}
return v1.PodUnknown
}
func (p *SFMeshProvider) appStateToPodConditions(state string, transitiontime metav1.Time) []v1.PodCondition {
switch state {
case "Succeeded":
return []v1.PodCondition{
v1.PodCondition{
Type: v1.PodReady,
Status: v1.ConditionTrue,
LastTransitionTime: transitiontime,
}, v1.PodCondition{
Type: v1.PodInitialized,
Status: v1.ConditionTrue,
LastTransitionTime: transitiontime,
}, v1.PodCondition{
Type: v1.PodScheduled,
Status: v1.ConditionTrue,
LastTransitionTime: transitiontime,
},
}
}
return []v1.PodCondition{}
}
func (p *SFMeshProvider) getMeshService(appName string, serviceName string) (servicefabricmesh.ServiceResourceDescription, error) {
svc, err := p.serviceClient.Get(context.Background(), p.resourceGroup, appName, serviceName)
if err != nil {
return servicefabricmesh.ServiceResourceDescription{}, err
}
return svc, err
}
func appStateToContainerState(state string, appStartTime metav1.Time) v1.ContainerState {
if state == "Succeeded" {
return v1.ContainerState{
Running: &v1.ContainerStateRunning{
StartedAt: appStartTime,
},
}
}
if state == "Failed" || state == "Canceled" {
return v1.ContainerState{
Terminated: &v1.ContainerStateTerminated{
ExitCode: 1,
Reason: "",
Message: "",
StartedAt: appStartTime,
FinishedAt: metav1.NewTime(time.Now()),
},
}
}
return v1.ContainerState{
Waiting: &v1.ContainerStateWaiting{
Reason: "",
Message: "",
},
}
}
func (p *SFMeshProvider) getMeshNetworkPublicIP(networkName string) (*string, error) {
network, err := p.networkClient.Get(context.Background(), p.resourceGroup, networkName)
if err != nil {
return nil, err
}
ipAddress := network.IngressConfig.PublicIPAddress
return ipAddress, nil
}
func (p *SFMeshProvider) applicationDescriptionToPod(app servicefabricmesh.ApplicationResourceDescription) (*v1.Pod, error) {
var podCreationTimestamp metav1.Time
if *app.Tags["CreationTimestamp"] != "" {
t, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", *app.Tags["CreationTimestamp"])
if err != nil {
return nil, err
}
podCreationTimestamp = metav1.NewTime(t)
}
containerStartTime := podCreationTimestamp
appState := app.ProvisioningState
podPhase := p.appStateToPodPhase(*appState)
podConditions := p.appStateToPodConditions(*appState, podCreationTimestamp)
service, err := p.getMeshService(*app.Name, (*app.ServiceNames)[0])
containers := []v1.Container{}
containerStatuses := []v1.ContainerStatus{}
for _, codePkg := range *service.CodePackages {
container := v1.Container{}
container.Name = *codePkg.Name
container.Image = *codePkg.Image
if codePkg.Commands != nil {
container.Command = *codePkg.Commands
}
container.Resources = v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse(fmt.Sprintf("%g", *codePkg.Resources.Requests.CPU)),
v1.ResourceMemory: resource.MustParse(fmt.Sprintf("%gG", *codePkg.Resources.Requests.MemoryInGB)),
},
}
if codePkg.Resources.Limits != nil {
container.Resources.Limits = v1.ResourceList{
v1.ResourceCPU: resource.MustParse(fmt.Sprintf("%g", *codePkg.Resources.Limits.CPU)),
v1.ResourceMemory: resource.MustParse(fmt.Sprintf("%gG", *codePkg.Resources.Limits.MemoryInGB)),
}
}
containerStatus := v1.ContainerStatus{
Name: *codePkg.Name,
State: appStateToContainerState(*appState, podCreationTimestamp),
Ready: podPhase == v1.PodRunning,
Image: container.Image,
ImageID: "",
ContainerID: "",
}
containerStatuses = append(containerStatuses, containerStatus)
containers = append(containers, container)
}
appName := app.Name
ipAddress := ""
meshIP, err := p.getMeshNetworkPublicIP(*appName)
if err != nil {
return nil, err
}
if meshIP != nil {
ipAddress = *meshIP
}
pod := v1.Pod{
TypeMeta: metav1.TypeMeta{
Kind: "Pod",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: *app.Tags["PodName"],
Namespace: *app.Tags["Namespace"],
ClusterName: *app.Tags["ClusterName"],
UID: types.UID(*app.Tags["UID"]),
CreationTimestamp: podCreationTimestamp,
},
Spec: v1.PodSpec{
NodeName: *app.Tags["NodeName"],
Volumes: []v1.Volume{},
Containers: containers,
},
Status: v1.PodStatus{
Phase: podPhase,
Conditions: podConditions,
Message: "",
Reason: "",
HostIP: "",
PodIP: ipAddress,
StartTime: &containerStartTime,
ContainerStatuses: containerStatuses,
},
}
return &pod, nil
}
// GetContainerLogs retrieves the logs of a container by name.
func (p *SFMeshProvider) GetContainerLogs(namespace, podName, containerName string, tail int) (string, error) {
log.Printf("receive GetContainerLogs %q\n", podName)
return "", nil
}
// GetPodFullName gets the full pod name as defined in the provider context
func (p *SFMeshProvider) GetPodFullName(namespace string, pod string) string {
return ""
}
// ExecInContainer executes a command in a container in the pod, copying data
// between in/out/err and the container's stdin/stdout/stderr.
func (p *SFMeshProvider) ExecInContainer(name string, uid types.UID, container string, cmd []string, in io.Reader, out, err io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error {
log.Printf("receive ExecInContainer %q\n", container)
return nil
}
// GetPodStatus returns the status of a pod by name that is "running".
// returns nil if a pod by that name is not found.
func (p *SFMeshProvider) GetPodStatus(namespace, name string) (*v1.PodStatus, error) {
pod, err := p.GetPod(namespace, name)
if err != nil {
return nil, err
}
if pod == nil {
return nil, nil
}
return &pod.Status, nil
}
// GetPods returns a list of all pods known to be running within SF Mesh.
func (p *SFMeshProvider) GetPods() ([]*v1.Pod, error) {
log.Printf("receive GetPods\n")
var pods []*v1.Pod
list, err := p.appClient.ListByResourceGroup(context.Background(), p.resourceGroup)
if err != nil {
return pods, err
}
apps := list.Values()
for _, app := range apps {
if app.Tags == nil {
continue
}
val, present := app.Tags["NodeName"]
if !present {
continue
}
if *val != p.nodeName {
continue
}
pod, err := p.applicationDescriptionToPod(app)
if err != nil {
return pods, err
}
pods = append(pods, pod)
}
return pods, nil
}
// Capacity returns a resource list containing the capacity limits set for SF Mesh.
func (p *SFMeshProvider) Capacity() v1.ResourceList {
return v1.ResourceList{
"cpu": resource.MustParse(defaultCPUCapacity),
"memory": resource.MustParse(defaultMemoryCapacity),
"pods": resource.MustParse(defaultPodCapacity),
}
}
// NodeConditions returns a list of conditions (Ready, OutOfDisk, etc), for updates to the node status
// within Kubernetes.
func (p *SFMeshProvider) NodeConditions() []v1.NodeCondition {
// TODO: Make this configurable
return []v1.NodeCondition{
{
Type: "Ready",
Status: v1.ConditionTrue,
LastHeartbeatTime: metav1.Now(),
LastTransitionTime: metav1.Now(),
Reason: "KubeletReady",
Message: "kubelet is ready.",
},
{
Type: "OutOfDisk",
Status: v1.ConditionFalse,
LastHeartbeatTime: metav1.Now(),
LastTransitionTime: metav1.Now(),
Reason: "KubeletHasSufficientDisk",
Message: "kubelet has sufficient disk space available",
},
{
Type: "MemoryPressure",
Status: v1.ConditionFalse,
LastHeartbeatTime: metav1.Now(),
LastTransitionTime: metav1.Now(),
Reason: "KubeletHasSufficientMemory",
Message: "kubelet has sufficient memory available",
},
{
Type: "DiskPressure",
Status: v1.ConditionFalse,
LastHeartbeatTime: metav1.Now(),
LastTransitionTime: metav1.Now(),
Reason: "KubeletHasNoDiskPressure",
Message: "kubelet has no disk pressure",
},
{
Type: "NetworkUnavailable",
Status: v1.ConditionFalse,
LastHeartbeatTime: metav1.Now(),
LastTransitionTime: metav1.Now(),
Reason: "RouteCreated",
Message: "RouteController created a route",
},
}
}
// NodeAddresses returns a list of addresses for the node status
// within Kubernetes.
func (p *SFMeshProvider) NodeAddresses() []v1.NodeAddress {
return []v1.NodeAddress{
{
Type: "InternalIP",
Address: p.internalIP,
},
}
}
// NodeDaemonEndpoints returns NodeDaemonEndpoints for the node status
// within Kubernetes.
func (p *SFMeshProvider) NodeDaemonEndpoints() *v1.NodeDaemonEndpoints {
return &v1.NodeDaemonEndpoints{
KubeletEndpoint: v1.DaemonEndpoint{
Port: p.daemonEndpointPort,
},
}
}
// OperatingSystem returns the operating system for this provider.
// This is a noop to default to Linux for now.
func (p *SFMeshProvider) OperatingSystem() string {
return providers.OperatingSystemLinux
}