Removed hardcode from protokube logic. Fixes #15. (#46)

This commit is contained in:
prashima 2017-04-11 13:38:38 -07:00 committed by Miao Luo
parent e191f7dd96
commit 074791b446
6 changed files with 208 additions and 55 deletions

View File

@ -93,7 +93,7 @@ Please note that dns-controller has also been modified to support vSphere. You c
Execute following command to launch cluster. Execute following command to launch cluster.
```bash ```bash
.build/dist/darwin/amd64/kops create cluster kubernetes.skydns.local --cloud=vsphere --zones=${AWS_REGION}a --dns-zone=skydns.local --networking=flannel .build/dist/darwin/amd64/kops create cluster kubernetes.skydns.local --cloud=vsphere --zones=vmware-zone --dns-zone=skydns.local --networking=flannel
--vsphere-server=10.160.97.44 --vsphere-datacenter=VSAN-DC --vsphere-resource-pool=VSAN-Cluster --vsphere-datastore=vsanDatastore --dns private --vsphere-coredns-server=http://10.192.217.24:2379 --image="ubuntu_16_04" --vsphere-server=10.160.97.44 --vsphere-datacenter=VSAN-DC --vsphere-resource-pool=VSAN-Cluster --vsphere-datastore=vsanDatastore --dns private --vsphere-coredns-server=http://10.192.217.24:2379 --image="ubuntu_16_04"
``` ```
@ -102,7 +102,7 @@ Use .build/dist/linux/amd64/kops if working on a linux machine, instead of mac.
**Notes** **Notes**
1. ```clustername``` should end with **skydns.local**. Example: ```kubernetes.cluster.skydns.local```. 1. ```clustername``` should end with **skydns.local**. Example: ```kubernetes.cluster.skydns.local```.
2. ```zones``` should end with ```a```. Example: ```us-west-2a``` or as the above command sets ```--zones=${AWS_REGION}a```. 2. For ```zones``` any string will do, for now. It's only getting used for the construction of names of various entities. But it's a mandatory argument.
3. Make sure following parameters have these values, 3. Make sure following parameters have these values,
* ```--dns-zone=skydns.local``` * ```--dns-zone=skydns.local```
* ```--networking=flannel``` * ```--networking=flannel```

View File

@ -57,6 +57,7 @@ func (b *AutoscalingGroupModelBuilder) Build(c *fi.ModelBuilderContext) error {
VM: createVmTask, VM: createVmTask,
IG: ig, IG: ig,
BootstrapScript: b.BootstrapScript, BootstrapScript: b.BootstrapScript,
EtcdClusters: b.Cluster.Spec.EtcdClusters,
} }
attachISOTask.BootstrapScript.AddAwsEnvironmentVariables = true attachISOTask.BootstrapScript.AddAwsEnvironmentVariables = true

View File

@ -18,37 +18,26 @@ package protokube
import ( import (
"errors" "errors"
"fmt"
"github.com/golang/glog" "github.com/golang/glog"
"io/ioutil"
"k8s.io/kops/upup/pkg/fi/cloudup/vsphere"
"net" "net"
"os/exec"
"runtime"
"strings"
) )
const EtcdDataKey = "01" const VolumeMetaDataFile = "/vol-metadata/metadata.json"
const EtcdDataVolPath = "/mnt/master-" + EtcdDataKey
const EtcdEventKey = "02"
const EtcdEventVolPath = "/mnt/master-" + EtcdEventKey
// TODO Use lsblk or counterpart command to find the actual device details.
const LocalDeviceForDataVol = "/dev/sdb1"
const LocalDeviceForEventsVol = "/dev/sdc1"
const VolStatusValue = "attached" const VolStatusValue = "attached"
const EtcdNodeName = "a"
const EtcdClusterName = "main"
const EtcdEventsClusterName = "events"
type VSphereVolumes struct { type VSphereVolumes struct{}
// Dummy property. Not getting used any where for now.
paths map[string]string
}
var _ Volumes = &VSphereVolumes{} var _ Volumes = &VSphereVolumes{}
var machineIp net.IP var machineIp net.IP
func NewVSphereVolumes() (*VSphereVolumes, error) { func NewVSphereVolumes() (*VSphereVolumes, error) {
vsphereVolumes := &VSphereVolumes{ vsphereVolumes := &VSphereVolumes{}
paths: make(map[string]string),
}
vsphereVolumes.paths[EtcdDataKey] = EtcdDataVolPath
vsphereVolumes.paths[EtcdEventKey] = EtcdEventVolPath
return vsphereVolumes, nil return vsphereVolumes, nil
} }
@ -60,51 +49,82 @@ func (v *VSphereVolumes) FindVolumes() ([]*Volume, error) {
attachedTo = ip.String() attachedTo = ip.String()
} }
// etcd data volume and etcd cluster spec. etcdClusters, err := getVolMetadata()
{
vol := &Volume{ if err != nil {
ID: EtcdDataKey, return nil, err
LocalDevice: LocalDeviceForDataVol,
AttachedTo: attachedTo,
Mountpoint: EtcdDataVolPath,
Status: VolStatusValue,
Info: VolumeInfo{
Description: EtcdClusterName,
},
}
etcdSpec := &EtcdClusterSpec{
ClusterKey: EtcdClusterName,
NodeName: EtcdNodeName,
NodeNames: []string{EtcdNodeName},
}
vol.Info.EtcdClusters = []*EtcdClusterSpec{etcdSpec}
volumes = append(volumes, vol)
} }
// etcd events volume and etcd events cluster spec. for _, etcd := range etcdClusters {
{ mountPoint := vsphere.GetMountPoint(etcd.VolumeId)
localDevice, err := getDevice(mountPoint)
if err != nil {
return nil, err
}
vol := &Volume{ vol := &Volume{
ID: EtcdEventKey, ID: etcd.VolumeId,
LocalDevice: LocalDeviceForEventsVol, LocalDevice: localDevice,
AttachedTo: attachedTo, AttachedTo: attachedTo,
Mountpoint: EtcdEventVolPath, Mountpoint: mountPoint,
Status: VolStatusValue, Status: VolStatusValue,
Info: VolumeInfo{ Info: VolumeInfo{
Description: EtcdEventsClusterName, Description: etcd.EtcdClusterName,
}, },
} }
etcdSpec := &EtcdClusterSpec{ etcdSpec := &EtcdClusterSpec{
ClusterKey: EtcdEventsClusterName, ClusterKey: etcd.EtcdClusterName,
NodeName: EtcdNodeName, NodeName: etcd.EtcdNodeName,
NodeNames: []string{EtcdNodeName},
} }
var nodeNames []string
for _, member := range etcd.Members {
nodeNames = append(nodeNames, member.Name)
}
etcdSpec.NodeNames = nodeNames
vol.Info.EtcdClusters = []*EtcdClusterSpec{etcdSpec} vol.Info.EtcdClusters = []*EtcdClusterSpec{etcdSpec}
volumes = append(volumes, vol) volumes = append(volumes, vol)
} }
glog.Infof("Found volumes: %v", volumes) glog.V(4).Infof("Found volumes: %v", volumes)
return volumes, nil return volumes, nil
} }
func getDevice(mountPoint string) (string, error) {
if runtime.GOOS == "linux" {
cmd := "lsblk"
arg := "-l"
out, err := exec.Command(cmd, arg).Output()
if err != nil {
return "", err
}
if Containerized {
mountPoint = PathFor(mountPoint)
}
lines := strings.Split(string(out), "\n")
for _, line := range lines {
if strings.Contains(line, mountPoint) {
lsblkOutput := strings.Split(line, " ")
glog.V(4).Infof("Found device: %v ", lsblkOutput[0])
return "/dev/" + lsblkOutput[0], nil
}
}
} else {
return "", fmt.Errorf("Failed to find device. OS %v is not supported for vSphere.", runtime.GOOS)
}
return "", fmt.Errorf("No device has been mounted on mountPoint %v.", mountPoint)
}
func getVolMetadata() ([]vsphere.VolumeMetadata, error) {
rawData, err := ioutil.ReadFile(PathFor(VolumeMetaDataFile))
if err != nil {
return nil, err
}
return vsphere.UnmarshalVolumeMetadata(string(rawData))
}
func (v *VSphereVolumes) AttachVolume(volume *Volume) error { func (v *VSphereVolumes) AttachVolume(volume *Volume) error {
// Currently this is a no-op for vSphere. The virtual disks should already be mounted on this VM. // Currently this is a no-op for vSphere. The virtual disks should already be mounted on this VM.
glog.Infof("All volumes should already be attached. No operation done.") glog.Infof("All volumes should already be attached. No operation done.")

View File

@ -0,0 +1,66 @@
/*
Copyright 2017 The Kubernetes Authors.
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 vsphere
import (
"encoding/json"
"strconv"
)
type VolumeMetadata struct {
// EtcdClusterName is the name of the etcd cluster (main, events etc)
EtcdClusterName string `json:"etcdClusterName,omitempty"`
// EtcdNodeName is the name of a node in etcd cluster for which this volume will be used
EtcdNodeName string `json:"etcdNodeName,omitempty"`
// EtcdMember stores the configurations for each member of the cluster
Members []EtcdMemberSpec `json:"etcdMembers,omitempty"`
// Volume id
VolumeId string `json:"volumeId,omitempty"`
}
type EtcdMemberSpec struct {
// Name is the name of the member within the etcd cluster
Name string `json:"name,omitempty"`
InstanceGroup string `json:"instanceGroup,omitempty"`
}
func MarshalVolumeMetadata(v []VolumeMetadata) (string, error) {
metadata, err := json.Marshal(v)
if err != nil {
return "", err
}
return string(metadata), nil
}
func UnmarshalVolumeMetadata(text string) ([]VolumeMetadata, error) {
var v []VolumeMetadata
err := json.Unmarshal([]byte(text), &v)
return v, err
}
func GetVolumeId(i int) string {
return "0" + strconv.Itoa(i)
}
/*
* GetMountPoint will return the mount point where the volume is expected to be mounted.
* This path would be /mnt/master-<volumeId>, eg: /mnt/master-01.
*/
func GetMountPoint(volumeId string) string {
return "/mnt/master-" + volumeId
}

View File

@ -42,6 +42,7 @@ type AttachISO struct {
VM *VirtualMachine VM *VirtualMachine
IG *kops.InstanceGroup IG *kops.InstanceGroup
BootstrapScript *model.BootstrapScript BootstrapScript *model.BootstrapScript
EtcdClusters []*kops.EtcdClusterSpec
} }
var _ fi.HasName = &AttachISO{} var _ fi.HasName = &AttachISO{}
@ -111,7 +112,7 @@ func (_ *AttachISO) RenderVSphere(t *vsphere.VSphereAPITarget, a, e, changes *At
return nil return nil
} }
func createUserData(startupStr string, dir string, dnsServer string, vmUUID string) error { func createUserData(changes *AttachISO, startupStr string, dir string, dnsServer string, vmUUID string) error {
// Update the startup script to add the extra spaces for // Update the startup script to add the extra spaces for
// indentation when copied to the user-data file. // indentation when copied to the user-data file.
strArray := strings.Split(startupStr, "\n") strArray := strings.Split(startupStr, "\n")
@ -140,10 +141,15 @@ func createUserData(startupStr string, dir string, dnsServer string, vmUUID stri
vmUUIDStr := " " + vmUUID + "\n" vmUUIDStr := " " + vmUUID + "\n"
data = strings.Replace(data, "$VM_UUID", vmUUIDStr, -1) data = strings.Replace(data, "$VM_UUID", vmUUIDStr, -1)
data, err = createVolumeScript(changes, data)
if err != nil {
return err
}
userDataFile := filepath.Join(dir, "user-data") userDataFile := filepath.Join(dir, "user-data")
glog.V(4).Infof("User data file content: %s", data) glog.V(4).Infof("User data file content: %s", data)
if err := ioutil.WriteFile(userDataFile, []byte(data), 0644); err != nil { if err = ioutil.WriteFile(userDataFile, []byte(data), 0644); err != nil {
glog.Errorf("Unable to write user-data into file %s", userDataFile) glog.Errorf("Unable to write user-data into file %s", userDataFile)
return err return err
} }
@ -151,6 +157,60 @@ func createUserData(startupStr string, dir string, dnsServer string, vmUUID stri
return nil return nil
} }
func createVolumeScript(changes *AttachISO, data string) (string, error) {
if changes.IG.Spec.Role != kops.InstanceGroupRoleMaster {
return strings.Replace(data, "$VOLUME_SCRIPT", " No volume metadata needed for "+string(changes.IG.Spec.Role)+".", -1), nil
}
volsString, err := getVolMetadata(changes)
if err != nil {
return "", err
}
return strings.Replace(data, "$VOLUME_SCRIPT", " "+volsString, -1), nil
}
func getVolMetadata(changes *AttachISO) (string, error) {
var volsMetadata []vsphere.VolumeMetadata
// Creating vsphere.VolumeMetadata using clusters EtcdClusterSpec
for i, etcd := range changes.EtcdClusters {
volMetadata := vsphere.VolumeMetadata{}
volMetadata.EtcdClusterName = etcd.Name
volMetadata.VolumeId = vsphere.GetVolumeId(i + 1)
var members []vsphere.EtcdMemberSpec
var thisNode string
for _, member := range etcd.Members {
if *member.InstanceGroup == changes.IG.Name {
thisNode = member.Name
}
etcdMember := vsphere.EtcdMemberSpec{
Name: member.Name,
InstanceGroup: *member.InstanceGroup,
}
members = append(members, etcdMember)
}
if thisNode == "" {
return "", fmt.Errorf("Failed to construct volume metadata for %v InstanceGroup.", changes.IG.Name)
}
volMetadata.EtcdNodeName = thisNode
volMetadata.Members = members
volsMetadata = append(volsMetadata, volMetadata)
}
glog.V(4).Infof("Marshaling master vol metadata : %v", volsMetadata)
volsString, err := vsphere.MarshalVolumeMetadata(volsMetadata)
glog.V(4).Infof("Marshaled master vol metadata: %v", volsString)
if err != nil {
return "", err
}
return volsString, nil
}
func createMetaData(dir string, vmName string) error { func createMetaData(dir string, vmName string) error {
data := strings.Replace(metaDataTemplate, "$INSTANCE_ID", uuid.NewUUID().String(), -1) data := strings.Replace(metaDataTemplate, "$INSTANCE_ID", uuid.NewUUID().String(), -1)
data = strings.Replace(data, "$LOCAL_HOST_NAME", vmName, -1) data = strings.Replace(data, "$LOCAL_HOST_NAME", vmName, -1)
@ -165,8 +225,9 @@ func createMetaData(dir string, vmName string) error {
return nil return nil
} }
func createISO(changes *AttachISO, startupStr string, dir string, dnsServer string, vmUUID string) (string, error) { func createISO(changes *AttachISO, startupStr string, dir string, dnsServer, vmUUID string) (string, error) {
err := createUserData(startupStr, dir, dnsServer, vmUUID) err := createUserData(changes, startupStr, dir, dnsServer, vmUUID)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -34,6 +34,11 @@ $VM_UUID
owner: root:root owner: root:root
path: /root/vm_uuid path: /root/vm_uuid
permissions: "0644" permissions: "0644"
- content: |
$VOLUME_SCRIPT
owner: root:root
path: /vol-metadata/metadata.json
permissions: "0644"
runcmd: runcmd:
- bash /root/update_dns.sh 2>&1 > /var/log/update_dns.log - bash /root/update_dns.sh 2>&1 > /var/log/update_dns.log