Skip to content
This repository has been archived by the owner on Nov 9, 2020. It is now read-only.

Commit

Permalink
Adds npipe server impl for windows (#1488)
Browse files Browse the repository at this point in the history
This commit adds an npipe/http server impl for servicing handshake and create volume APIs, and enables esx_vmdkcmd and vmdkops usage on Windows environments.

* Adds NpipePluginServer impl with handshake and create volume handlers.
* Adds default config for windows.
* Enables vmdkops usage under windows environments.
* Extracts main module constants into constants.go.
* Adds vmci_client_proxy.c for conditionally importing vmci_client.c.
  • Loading branch information
venilnoronha committed Jun 28, 2017
1 parent f94d9cb commit fa7944f
Show file tree
Hide file tree
Showing 10 changed files with 263 additions and 12 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ src/dvolplug/dvolplug
*~
.*~
esx_service/.lint_*
*.swp
4 changes: 4 additions & 0 deletions esx_service/vmci/vmci_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef __VMCI_CLIENT_H__
#define __VMCI_CLIENT_H__

//
// VMCI sockets communication - client side.
Expand Down Expand Up @@ -69,3 +71,5 @@ Vmci_GetReply(int port, const char* json_request, const char* be_name, be_answer
//
void
Vmci_FreeBuf(be_answer *ans);

#endif /* __VMCI_CLIENT_H__ */
21 changes: 21 additions & 0 deletions esx_service/vmci/vmci_client_proxy.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2017 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//
// This file serves as proxy for conditionally importing vmci_client.c
//

#ifndef _WIN32
#include "vmci_client.c"
#endif
36 changes: 36 additions & 0 deletions vmdk_plugin/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2016-2017 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

const (
// vDVS drivers.
photonDriver = "photon"
vmdkDriver = "vmdk" // deprecated
vsphereDriver = "vsphere"

// Default ESX service port.
defaultPort = 1019

// Docker driver types.
volumeDriver = "VolumeDriver"

// Docker plugin handshake endpoint.
// Also see https://docs.docker.com/engine/extend/plugin_api/#handshake-api
pluginActivatePath = "/Plugin.Activate"

// Docker volume plugin endpoints.
// Also see https://docs.docker.com/engine/extend/plugins_volume/#volume-plugin-protocol
volumeDriverCreatePath = "/VolumeDriver.Create"
)
8 changes: 5 additions & 3 deletions vmdk_plugin/drivers/vmdk/vmdkops/esx_vmdkcmd.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2016 VMware, Inc. All Rights Reserved.
// Copyright 2016-2017 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// +build linux
// +build linux windows

// The default (ESX) implementation of the VmdkCmdRunner interface.
// This implementation sends synchronous commands to and receives responses from ESX.
Expand All @@ -34,7 +34,9 @@ import (

/*
#cgo CFLAGS: -I ../../../../esx_service/vmci
#include "vmci_client.c"
#cgo windows LDFLAGS: -L. -lvmci_client
#include "vmci_client.h"
#include "vmci_client_proxy.c"
*/
import "C"

Expand Down
4 changes: 2 additions & 2 deletions vmdk_plugin/drivers/vmdk/vmdkops/vmdkops.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2016 VMware, Inc. All Rights Reserved.
// Copyright 2016-2017 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// +build linux
// +build linux windows

package vmdkops

Expand Down
17 changes: 10 additions & 7 deletions vmdk_plugin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"os"
"os/signal"
"reflect"
"runtime"
"syscall"

log "github.com/Sirupsen/logrus"
Expand All @@ -32,13 +33,6 @@ import (
"github.com/vmware/docker-volume-vsphere/vmdk_plugin/utils/config"
)

const (
photonDriver = "photon"
vmdkDriver = "vmdk"
vsphereDriver = "vsphere"
defaultPort = 1019
)

// PluginServer responds to HTTP requests from Docker.
type PluginServer interface {
// Init initializes the server.
Expand Down Expand Up @@ -135,6 +129,15 @@ func main() {
}
}

// The windows plugin only supports the vsphere driver.
if runtime.GOOS == "windows" && *driverName != vsphereDriver {
msg := fmt.Sprintf("Plugin only supports the %s driver on Windows, ignoring parameter driver = %s.",
vsphereDriver, c.Driver)
log.Warning(msg)
fmt.Println(msg)
*driverName = vsphereDriver
}

log.WithFields(log.Fields{
"driver": *driverName,
"log_level": *logLevel,
Expand Down
130 changes: 130 additions & 0 deletions vmdk_plugin/main_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright 2017 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

// A VMDK Docker Data Volume plugin implementation for Windows OS.

import (
"encoding/json"
"fmt"
"net"
"net/http"
"os"
"path/filepath"

"github.com/Microsoft/go-winio"
log "github.com/Sirupsen/logrus"
"github.com/docker/go-plugins-helpers/volume"
)

const (
npipeAddr = `\\.\pipe\vsphere-dvs` // Plugin's npipe address
)

var (
mountRoot = filepath.Join(os.Getenv("LOCALAPPDATA"), "docker-volume-vsphere", "mounts") // VMDK volumes are mounted here
)

// NpipePluginServer serves HTTP requests from Docker over windows npipe.
type NpipePluginServer struct {
PluginServer
driver *volume.Driver // The driver implementation
mux *http.ServeMux // The HTTP mux
listener net.Listener // The npipe listener
}

// Response for /Plugin.Activate to activate the Docker plugin.
type PluginActivateResponse struct {
Implements []string // Slice of implemented driver types
}

// NewPluginServer returns a new instance of NpipePluginServer.
func NewPluginServer(driverName string, driver *volume.Driver) *NpipePluginServer {
return &NpipePluginServer{driver: driver, mux: http.NewServeMux()}
}

// writeJSON writes the JSON encoding of resp to the writer.
func writeJSON(resp interface{}, writer *http.ResponseWriter) error {
bytes, err := json.Marshal(resp)
if err == nil {
fmt.Fprintf(*writer, string(bytes))
}
return err
}

// writeError writes an error message and status to the writer.
func writeError(path string, writer http.ResponseWriter, req *http.Request, status int, err error) {
log.WithFields(log.Fields{"path": path, "req": req, "status": status,
"err": err}).Error("Failed to service request ")
http.Error(writer, err.Error(), status)
}

// PluginActivate writes the plugin's handshake response to the writer.
func (s *NpipePluginServer) PluginActivate(writer http.ResponseWriter, req *http.Request) {
resp := &PluginActivateResponse{Implements: []string{volumeDriver}}
errJSON := writeJSON(resp, &writer)
if errJSON != nil {
writeError(pluginActivatePath, writer, req, http.StatusInternalServerError, errJSON)
return
}
log.WithFields(log.Fields{"resp": resp}).Info("Plugin activated ")
}

// VolumeDriverCreate creates a volume and writes the response to the writer.
func (s *NpipePluginServer) VolumeDriverCreate(writer http.ResponseWriter, req *http.Request) {
var volumeReq volume.Request
err := json.NewDecoder(req.Body).Decode(&volumeReq)
if err != nil {
writeError(volumeDriverCreatePath, writer, req, http.StatusBadRequest, err)
return
}

resp := (*s.driver).Create(volumeReq)
errJSON := writeJSON(resp, &writer)
if errJSON != nil {
writeError(volumeDriverCreatePath, writer, req, http.StatusInternalServerError, errJSON)
return
}
log.WithFields(log.Fields{"resp": resp}).Info("Serviced /VolumeDriver.Create ")
}

// registerHttpHandlers registers handlers with the HTTP mux.
func (s *NpipePluginServer) registerHandlers() {
s.mux.HandleFunc(pluginActivatePath, s.PluginActivate)
s.mux.HandleFunc(volumeDriverCreatePath, s.VolumeDriverCreate)
}

// Init initializes the npipe listener which serves HTTP requests
// from Docker using the HTTP mux.
func (s *NpipePluginServer) Init() {
var err error
s.listener, err = winio.ListenPipe(npipeAddr, nil)
if err != nil {
msg := fmt.Sprintf("Failed to initialize npipe at %s - exiting", npipeAddr)
log.WithFields(log.Fields{"err": err}).Fatal(msg)
fmt.Println(msg)
os.Exit(1)
}

s.registerHandlers()
log.WithFields(log.Fields{"npipe": npipeAddr}).Info("Going into Serve - Listening on npipe ")
log.Info(http.Serve(s.listener, s.mux))
}

// Destroy shuts down the npipe listener.
func (s *NpipePluginServer) Destroy() {
log.WithFields(log.Fields{"npipe": npipeAddr}).Info("Closing npipe listener ")
s.listener.Close()
}
27 changes: 27 additions & 0 deletions vmdk_plugin/utils/config/config_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2017 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import (
"os"
"path/filepath"
)

var (
// DefaultConfigPath is the default location of the config file.
DefaultConfigPath = filepath.Join(os.Getenv("PROGRAMDATA"), "docker-volume-vsphere", "docker-volume-vsphere.conf")
// DefaultLogPath is the default location of the log (trace) file.
DefaultLogPath = filepath.Join(os.Getenv("LOCALAPPDATA"), "docker-volume-vsphere", "logs", "docker-volume-vsphere.log")
)
27 changes: 27 additions & 0 deletions vmdk_plugin/utils/config/config_windows_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2017 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config_test

import (
"github.com/stretchr/testify/assert"
"github.com/vmware/docker-volume-vsphere/vmdk_plugin/utils/config"
"os"
"testing"
)

func TestWindowsPath(t *testing.T) {
assert.Equal(t, config.DefaultConfigPath, os.Getenv("PROGRAMDATA")+`\docker-volume-vsphere\docker-volume-vsphere.conf`)
assert.Equal(t, config.DefaultLogPath, os.Getenv("LOCALAPPDATA")+`\docker-volume-vsphere\logs\docker-volume-vsphere.log`)
}

0 comments on commit fa7944f

Please sign in to comment.