mirror of https://github.com/kubernetes/kops.git
Merge remote-tracking branch 'upstream/master' into update_aws-sdk
This commit is contained in:
commit
498e3b29d7
2
OWNERS
2
OWNERS
|
|
@ -5,3 +5,5 @@ approvers:
|
|||
- zmerlynn
|
||||
- andrewsykim
|
||||
- geojaz
|
||||
- kashifsaadat
|
||||
- gambol99
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import (
|
|||
|
||||
var (
|
||||
create_secret_dockerconfig_long = templates.LongDesc(i18n.T(`
|
||||
Create a new docker config, and store it in the state store.
|
||||
Create a new docker config, and store it in the state store.
|
||||
Used to configure docker on each master or node (ie. for auth)
|
||||
Use update to modify it, this command will only create a new entry.`))
|
||||
|
||||
|
|
@ -40,6 +40,9 @@ var (
|
|||
# Create an new docker config.
|
||||
kops create secret dockerconfig -f /path/to/docker/config.json \
|
||||
--name k8s-cluster.example.com --state s3://example.com
|
||||
# Replace an existing docker config secret.
|
||||
kops create secret dockerconfig -f /path/to/docker/config.json --force \
|
||||
--name k8s-cluster.example.com --state s3://example.com
|
||||
`))
|
||||
|
||||
create_secret_dockerconfig_short = i18n.T(`Create a docker config.`)
|
||||
|
|
@ -48,6 +51,7 @@ var (
|
|||
type CreateSecretDockerConfigOptions struct {
|
||||
ClusterName string
|
||||
DockerConfigPath string
|
||||
Force bool
|
||||
}
|
||||
|
||||
func NewCmdCreateSecretDockerConfig(f *util.Factory, out io.Writer) *cobra.Command {
|
||||
|
|
@ -78,6 +82,7 @@ func NewCmdCreateSecretDockerConfig(f *util.Factory, out io.Writer) *cobra.Comma
|
|||
}
|
||||
|
||||
cmd.Flags().StringVarP(&options.DockerConfigPath, "", "f", "", "Path to docker config JSON file")
|
||||
cmd.Flags().BoolVar(&options.Force, "force", options.Force, "Force replace the kops secret if it already exists")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
@ -119,9 +124,19 @@ func RunCreateSecretDockerConfig(f *util.Factory, out io.Writer, options *Create
|
|||
|
||||
secret.Data = data
|
||||
|
||||
_, _, err = secretStore.GetOrCreateSecret("dockerconfig", secret)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error adding docker config secret: %v", err)
|
||||
if !options.Force {
|
||||
_, created, err := secretStore.GetOrCreateSecret("dockerconfig", secret)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error adding dockerconfig secret: %v", err)
|
||||
}
|
||||
if !created {
|
||||
return fmt.Errorf("failed to create the dockerconfig secret as it already exists. The `--force` flag can be passed to replace an existing secret.")
|
||||
}
|
||||
} else {
|
||||
_, err := secretStore.ReplaceSecret("dockerconfig", secret)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error updating dockerconfig secret: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@ var (
|
|||
# Create a new encryption config.
|
||||
kops create secret encryptionconfig -f config.yaml \
|
||||
--name k8s-cluster.example.com --state s3://example.com
|
||||
# Replace an existing encryption config secret.
|
||||
kops create secret encryptionconfig -f config.yaml --force \
|
||||
--name k8s-cluster.example.com --state s3://example.com
|
||||
`))
|
||||
|
||||
create_secret_encryptionconfig_short = i18n.T(`Create an encryption config.`)
|
||||
|
|
@ -48,6 +51,7 @@ var (
|
|||
type CreateSecretEncryptionConfigOptions struct {
|
||||
ClusterName string
|
||||
EncryptionConfigPath string
|
||||
Force bool
|
||||
}
|
||||
|
||||
func NewCmdCreateSecretEncryptionConfig(f *util.Factory, out io.Writer) *cobra.Command {
|
||||
|
|
@ -78,6 +82,7 @@ func NewCmdCreateSecretEncryptionConfig(f *util.Factory, out io.Writer) *cobra.C
|
|||
}
|
||||
|
||||
cmd.Flags().StringVarP(&options.EncryptionConfigPath, "", "f", "", "Path to encryption config yaml file")
|
||||
cmd.Flags().BoolVar(&options.Force, "force", options.Force, "Force replace the kops secret if it already exists")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
@ -120,9 +125,19 @@ func RunCreateSecretEncryptionConfig(f *util.Factory, out io.Writer, options *Cr
|
|||
|
||||
secret.Data = data
|
||||
|
||||
_, _, err = secretStore.GetOrCreateSecret("encryptionconfig", secret)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error adding encryption config secret: %v", err)
|
||||
if !options.Force {
|
||||
_, created, err := secretStore.GetOrCreateSecret("encryptionconfig", secret)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error adding encryptionconfig secret: %v", err)
|
||||
}
|
||||
if !created {
|
||||
return fmt.Errorf("failed to create the encryptionconfig secret as it already exists. The `--force` flag can be passed to replace an existing secret.")
|
||||
}
|
||||
} else {
|
||||
_, err := secretStore.ReplaceSecret("encryptionconfig", secret)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error updating encryptionconfig secret: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
|
@ -31,10 +32,18 @@ import (
|
|||
"k8s.io/kops/cmd/kops/util"
|
||||
api "k8s.io/kops/pkg/apis/kops"
|
||||
apiutil "k8s.io/kops/pkg/apis/kops/util"
|
||||
"k8s.io/kops/pkg/dns"
|
||||
"k8s.io/kops/pkg/validation"
|
||||
"k8s.io/kops/util/pkg/tables"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if runtime.GOOS == "darwin" {
|
||||
// In order for net.LookupHost(apiAddr.Host) to lookup our placeholder address on darwin, we have to
|
||||
os.Setenv("GODEBUG", "netdns=go")
|
||||
}
|
||||
}
|
||||
|
||||
type ValidateClusterOptions struct {
|
||||
// No options yet
|
||||
}
|
||||
|
|
@ -105,6 +114,25 @@ func RunValidateCluster(f *util.Factory, cmd *cobra.Command, args []string, out
|
|||
return fmt.Errorf("Cannot build kube api client for %q: %v\n", contextName, err)
|
||||
}
|
||||
|
||||
// Do not use if we are running gossip
|
||||
if !dns.IsGossipHostname(cluster.ObjectMeta.Name) {
|
||||
hasPlaceHolderIPAddress, err := validation.HasPlaceHolderIP(contextName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hasPlaceHolderIPAddress {
|
||||
fmt.Println(
|
||||
"Validation Failed\n\n" +
|
||||
"The dns-controller Kubernetes deployment has not updated the Kubernetes cluster's API DNS entry to the correct IP address." +
|
||||
" The API DNS IP address is the placeholder address that kops creates: 203.0.113.123." +
|
||||
" Please wait about 5-10 minutes for a master to start, dns-controller to launch, and DNS to propagate." +
|
||||
" The protokube container and dns-controller deployment logs may contain more diagnostic information." +
|
||||
" Etcd and the API DNS entries must be updated for a kops Kubernetes cluster to start.")
|
||||
return fmt.Errorf("\nCannot reach cluster's API server: unable to Validate Cluster: %s", cluster.ObjectMeta.Name)
|
||||
}
|
||||
}
|
||||
|
||||
validationCluster, validationFailed := validation.ValidateCluster(cluster.ObjectMeta.Name, list, k8sClient)
|
||||
|
||||
if validationCluster == nil || validationCluster.NodeList == nil || validationCluster.NodeList.Items == nil {
|
||||
|
|
|
|||
|
|
@ -20,12 +20,16 @@ kops create secret dockerconfig
|
|||
# Create an new docker config.
|
||||
kops create secret dockerconfig -f /path/to/docker/config.json \
|
||||
--name k8s-cluster.example.com --state s3://example.com
|
||||
# Replace an existing docker config secret.
|
||||
kops create secret dockerconfig -f /path/to/docker/config.json --force \
|
||||
--name k8s-cluster.example.com --state s3://example.com
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-f, -- string Path to docker config JSON file
|
||||
--force Force replace the kops secret if it already exists
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
|||
|
|
@ -20,12 +20,16 @@ kops create secret encryptionconfig
|
|||
# Create a new encryption config.
|
||||
kops create secret encryptionconfig -f config.yaml \
|
||||
--name k8s-cluster.example.com --state s3://example.com
|
||||
# Replace an existing encryption config secret.
|
||||
kops create secret encryptionconfig -f config.yaml --force \
|
||||
--name k8s-cluster.example.com --state s3://example.com
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-f, -- string Path to encryption config yaml file
|
||||
--force Force replace the kops secret if it already exists
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Cluster Templating
|
||||
|
||||
The command `kops replace` can replace a cluster desired configuration from the config in a yaml file (see [/cli/kops_replace.md](/cli/kops_replace.md)).
|
||||
The command `kops replace` can replace a cluster desired configuration from the config in a yaml file (see [cli/kops_replace.md](cli/kops_replace.md)).
|
||||
|
||||
It is possible to generate that yaml file from a template, using the command `kops toolbox template` (see [cli/kops_toolbox_template.md](cli/kops_toolbox_template.md)).
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ Running `kops toolbox template` replaces the placeholders in the template by val
|
|||
|
||||
Note: when creating a cluster desired configuration template, you can
|
||||
|
||||
- use `kops get k8s-cluster.example.com -o yaml > cluster-desired-config.yaml` to create the cluster desired configuration file (see [cli/kops_get.md](cli/kops_get.md)). The values in this file are defined in [cli/cluster_spec.md](cli/cluster_spec.md).
|
||||
- use `kops get k8s-cluster.example.com -o yaml > cluster-desired-config.yaml` to create the cluster desired configuration file (see [cli/kops_get.md](cli/kops_get.md)). The values in this file are defined in [cluster_spec.md](cluster_spec.md).
|
||||
- replace values by placeholders in that file to create the template.
|
||||
|
||||
### Templates
|
||||
|
|
|
|||
|
|
@ -1,3 +1,26 @@
|
|||
# Installing Kops via Hombrew
|
||||
|
||||
Homebrew makes installing kops [very simple for MacOS.](../install.md)
|
||||
```bash
|
||||
brew update && brew install kops
|
||||
```
|
||||
|
||||
Development Releases and master can also be installed via Homebrew very easily:
|
||||
```bash
|
||||
# Development Release
|
||||
brew update && brew install kops --devel
|
||||
# HEAD of master
|
||||
brew update && brew install kops --HEAD
|
||||
```
|
||||
|
||||
Note: if you already have kops installed, you need to substitute `upgrade` for `install`.
|
||||
|
||||
You can switch between development and stable releases with:
|
||||
```bash
|
||||
brew switch kops 1.7.1
|
||||
brew switch kops 1.8.0-beta.1
|
||||
```
|
||||
|
||||
# Releasing kops to Brew
|
||||
|
||||
Submitting a new release of kops to Homebrew is very simple.
|
||||
|
|
@ -8,11 +31,20 @@ Submitting a new release of kops to Homebrew is very simple.
|
|||
This will automatically update the provided fields and open a PR for you.
|
||||
More details on this script are located [here.](https://github.com/Homebrew/brew/blob/master/Library/Homebrew/dev-cmd/bump-formula-pr.rb)
|
||||
|
||||
We now include both major and development releases in homebrew. A development version can be updated by adding the `--devel` flag.
|
||||
|
||||
Example usage:
|
||||
```
|
||||
```bash
|
||||
# Major Version
|
||||
brew bump-formula-pr \
|
||||
--url=https://github.com/kubernetes/kops/archive/1.7.1.tar.gz \
|
||||
--sha256=044c5c7a737ed3acf53517e64bb27d3da8f7517d2914df89efeeaf84bc8a722a
|
||||
|
||||
# Development Version
|
||||
brew bump-formula-pr \
|
||||
--devel \
|
||||
--url=https://github.com/kubernetes/kops/archive/1.8.0-beta.1.tar.gz \
|
||||
--sha256=81026d6c1cd7b3898a88275538a7842b4bd8387775937e0528ccb7b83948abf1
|
||||
```
|
||||
|
||||
* Update the URL variable to the tar.gz of the new release source code
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ From Homebrew:
|
|||
brew update && brew install kops
|
||||
```
|
||||
|
||||
Developers can also easily install [development releases](development/homebrew.md).
|
||||
|
||||
From Github:
|
||||
|
||||
```bash
|
||||
|
|
|
|||
|
|
@ -14,6 +14,13 @@ or `--networking flannel-udp` can be specified to explicitly choose a backend mo
|
|||
See the *Changes to k8s-policy* section in the
|
||||
[Calico release notes](https://github.com/projectcalico/calico/releases/tag/v2.4.0)
|
||||
for help.
|
||||
* Due to `ThirdPartyResources` becoming fully deprecated in Kubernetes v1.8 (replaced by `CustomResourceDefinitions`), existing Canal users upgrading their Clusters to Kubernetes v1.8 must follow the below TPR->CRD migration steps:
|
||||
1. Run: `kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v2.6.2/upgrade/v2.5/manifests/upgrade-job.yaml`
|
||||
2. Retrieve the pod name from describing the job: `kubectl describe job/calico-upgrade-v2.5`
|
||||
3. Validate the last log line from the pod reports that it completed successfully: `kubectl logs calico-upgrade-v2.5-<random-id>`
|
||||
4. Update the `KubernetesVersion` within your ClusterSpec to v1.8 (or above), performing an update & rolling-update to all nodes (will involve downtime)
|
||||
5. Confirm cluster is back up and all canal pods are running successfully: `kops validate cluster` (this may take a few minutes for the cluster to fully validate)
|
||||
6. Delete the upgrade job as it is no longer required: `kubectl delete job calico-upgrade-v2.5` (you can also safely delete the `clusterrole`, `clusterrolebinding` and `serviceaccount` resources that were created by the above manifest file)
|
||||
|
||||
# Full changelist
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,33 @@
|
|||
## How to update Kops - Kubernetes Ops
|
||||
# Updating kops (Binaries)
|
||||
|
||||
Update the latest source code from kubernetes/kops
|
||||
## MacOS
|
||||
|
||||
```
|
||||
cd ${GOPATH}/src/k8s.io/kops/
|
||||
git pull && make
|
||||
```
|
||||
From Homebrew:
|
||||
|
||||
Alternatively, if you installed from Homebrew
|
||||
```
|
||||
```bash
|
||||
brew update && brew upgrade kops
|
||||
```
|
||||
|
||||
From Github:
|
||||
|
||||
```bash
|
||||
rm -rf /usr/local/bin/kops
|
||||
wget -O kops https://github.com/kubernetes/kops/releases/download/$(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest | grep tag_name | cut -d '"' -f 4)/kops-darwin-amd64
|
||||
chmod +x ./kops
|
||||
sudo mv ./kops /usr/local/bin/
|
||||
```
|
||||
|
||||
You can also rerun rerun [these steps](development/building.md) if previously built from source.
|
||||
|
||||
## Linux
|
||||
|
||||
From Github:
|
||||
|
||||
```bash
|
||||
rm -rf /usr/local/bin/kops
|
||||
wget -O kops https://github.com/kubernetes/kops/releases/download/$(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest | grep tag_name | cut -d '"' -f 4)/kops-linux-amd64
|
||||
chmod +x ./kops
|
||||
sudo mv ./kops /usr/local/bin/
|
||||
```
|
||||
|
||||
You can also rerun rerun [these steps](development/building.md) if previously built from source.
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ k8s.io/kops/upup/pkg/fi/cloudup/dotasks
|
|||
k8s.io/kops/upup/pkg/fi/cloudup/gce
|
||||
k8s.io/kops/upup/pkg/fi/cloudup/gcetasks
|
||||
k8s.io/kops/upup/pkg/fi/cloudup/openstack
|
||||
k8s.io/kops/upup/pkg/fi/cloudup/openstacktasks
|
||||
k8s.io/kops/upup/pkg/fi/cloudup/terraform
|
||||
k8s.io/kops/upup/pkg/fi/cloudup/vsphere
|
||||
k8s.io/kops/upup/pkg/fi/cloudup/vspheretasks
|
||||
|
|
|
|||
|
|
@ -431,6 +431,57 @@ var dockerVersions = []dockerVersion{
|
|||
Hash: "4659c937b66519c88ef2a82a906bb156db29d191",
|
||||
Dependencies: []string{"policycoreutils-python"},
|
||||
},
|
||||
// 17.09.0 - k8s 1.8
|
||||
|
||||
// 17.09.0 - Jessie
|
||||
{
|
||||
DockerVersion: "17.09.0",
|
||||
Name: "docker-ce",
|
||||
Distros: []distros.Distribution{distros.DistributionJessie},
|
||||
Architectures: []Architecture{ArchitectureAmd64},
|
||||
Version: "17.09.0~ce-0~debian",
|
||||
Source: "http://download.docker.com/linux/debian/dists/jessie/pool/stable/amd64/docker-ce_17.09.0~ce-0~debian_amd64.deb",
|
||||
Hash: "430ba87f8aa36fedcac1a48e909cbe1830b53845",
|
||||
Dependencies: []string{"bridge-utils", "libapparmor1", "libltdl7", "perl"},
|
||||
},
|
||||
|
||||
// 17.09.0 - Jessie on ARM
|
||||
{
|
||||
DockerVersion: "17.09.0",
|
||||
Name: "docker-ce",
|
||||
Distros: []distros.Distribution{distros.DistributionJessie},
|
||||
Architectures: []Architecture{ArchitectureArm},
|
||||
Version: "17.09.0~ce-0~debian",
|
||||
Source: "http://download.docker.com/linux/debian/dists/jessie/pool/stable/armhf/docker-ce_17.09.0~ce-0~debian_armhf.deb",
|
||||
Hash: "5001a1defec7c33aa58ddebbd3eae6ebb5f36479",
|
||||
Dependencies: []string{"bridge-utils", "libapparmor1", "libltdl7", "perl"},
|
||||
},
|
||||
|
||||
// 17.09.0 - Xenial
|
||||
{
|
||||
DockerVersion: "17.09.0",
|
||||
Name: "docker-ce",
|
||||
Distros: []distros.Distribution{distros.DistributionXenial},
|
||||
Architectures: []Architecture{ArchitectureAmd64},
|
||||
Version: "17.09.0~ce-0~ubuntu",
|
||||
Source: "http://download.docker.com/linux/ubuntu/dists/xenial/pool/stable/amd64/docker-ce_17.09.0~ce-0~ubuntu_amd64.deb",
|
||||
Hash: "94f6e89be6d45d9988269a237eb27c7d6a844d7f",
|
||||
Dependencies: []string{"bridge-utils", "iptables", "libapparmor1", "libltdl7", "perl"},
|
||||
//Depends: iptables, init-system-helpers, lsb-base, libapparmor1, libc6, libdevmapper1.02.1, libltdl7, libeseccomp2, libsystemd0
|
||||
//Recommends: aufs-tools, ca-certificates, cgroupfs-mount | cgroup-lite, git, xz-utils, apparmor
|
||||
},
|
||||
|
||||
// 17.09.0 - Centos / Rhel7
|
||||
{
|
||||
DockerVersion: "17.09.0",
|
||||
Name: "docker-ce",
|
||||
Distros: []distros.Distribution{distros.DistributionRhel7, distros.DistributionCentos7},
|
||||
Architectures: []Architecture{ArchitectureAmd64},
|
||||
Version: "17.09.0.ce",
|
||||
Source: "https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-17.09.0.ce-1.el7.centos.x86_64.rpm",
|
||||
Hash: "b4ce72e80ff02926de943082821bbbe73958f87a",
|
||||
Dependencies: []string{"libtool-ltdl", "libseccomp", "libgcroup"},
|
||||
},
|
||||
}
|
||||
|
||||
func (d *dockerVersion) matches(arch Architecture, dockerVersion string, distro distros.Distribution) bool {
|
||||
|
|
|
|||
|
|
@ -322,7 +322,7 @@ type KubeControllerManagerConfig struct {
|
|||
// HorizontalPodAutoscalerUpscaleDelay is a duration that specifies how
|
||||
// long the autoscaler has to wait before another upscale operation can
|
||||
// be performed after the current one has completed.
|
||||
HorizontalPodAutoscalerUpscaleDelay *metav1.Duration `json:"horizontalPodAutoscalerUpscaleDelay,omitempty" flag:"horizontal-pod-autoscaler-downscale-delay"`
|
||||
HorizontalPodAutoscalerUpscaleDelay *metav1.Duration `json:"horizontalPodAutoscalerUpscaleDelay,omitempty" flag:"horizontal-pod-autoscaler-upscale-delay"`
|
||||
// FeatureGates is set of key=value pairs that describe feature gates for alpha/experimental features.
|
||||
FeatureGates map[string]string `json:"featureGates,omitempty" flag:"feature-gates"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -322,7 +322,7 @@ type KubeControllerManagerConfig struct {
|
|||
// HorizontalPodAutoscalerUpscaleDelay is a duration that specifies how
|
||||
// long the autoscaler has to wait before another upscale operation can
|
||||
// be performed after the current one has completed.
|
||||
HorizontalPodAutoscalerUpscaleDelay *metav1.Duration `json:"horizontalPodAutoscalerUpscaleDelay,omitempty" flag:"horizontal-pod-autoscaler-downscale-delay"`
|
||||
HorizontalPodAutoscalerUpscaleDelay *metav1.Duration `json:"horizontalPodAutoscalerUpscaleDelay,omitempty" flag:"horizontal-pod-autoscaler-upscale-delay"`
|
||||
// FeatureGates is set of key=value pairs that describe feature gates for alpha/experimental features.
|
||||
FeatureGates map[string]string `json:"featureGates,omitempty" flag:"feature-gates"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -469,9 +469,9 @@ func ValidateCluster(c *kops.Cluster, strict bool) *field.Error {
|
|||
}
|
||||
}
|
||||
|
||||
if kubernetesRelease.LT(semver.MustParse("1.6.0")) {
|
||||
if kubernetesRelease.LT(semver.MustParse("1.7.0")) {
|
||||
if c.Spec.Networking != nil && c.Spec.Networking.Romana != nil {
|
||||
return field.Invalid(fieldSpec.Child("Networking"), "romana", "romana networking is not supported with kubernetes versions 1.5 or lower")
|
||||
return field.Invalid(fieldSpec.Child("Networking"), "romana", "romana networking is not supported with kubernetes versions 1.6 or lower")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ go_library(
|
|||
"//upup/pkg/fi/cloudup/dotasks:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/gce:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/gcetasks:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/openstack:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/openstacktasks:go_default_library",
|
||||
"//upup/pkg/fi/fitasks:go_default_library",
|
||||
"//util/pkg/vfs:go_default_library",
|
||||
"//vendor/github.com/blang/semver:go_default_library",
|
||||
|
|
|
|||
|
|
@ -661,7 +661,7 @@ func addMasterASPolicies(p *Policy, resource stringorslice.StringOrSlice, legacy
|
|||
Resource: resource,
|
||||
Condition: Condition{
|
||||
"StringEquals": map[string]string{
|
||||
"ec2:ResourceTag/KubernetesCluster": clusterName,
|
||||
"autoscaling:ResourceTag/KubernetesCluster": clusterName,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@
|
|||
],
|
||||
"Condition": {
|
||||
"StringEquals": {
|
||||
"ec2:ResourceTag/KubernetesCluster": "iam-builder-test.k8s.local"
|
||||
"autoscaling:ResourceTag/KubernetesCluster": "iam-builder-test.k8s.local"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@
|
|||
],
|
||||
"Condition": {
|
||||
"StringEquals": {
|
||||
"ec2:ResourceTag/KubernetesCluster": "iam-builder-test.k8s.local"
|
||||
"autoscaling:ResourceTag/KubernetesCluster": "iam-builder-test.k8s.local"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ import (
|
|||
"k8s.io/kops/upup/pkg/fi/cloudup/dotasks"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/gce"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/gcetasks"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/openstacktasks"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -95,6 +97,11 @@ func (b *MasterVolumeBuilder) Build(c *fi.ModelBuilderContext) error {
|
|||
b.addVSphereVolume(c, name, volumeSize, zone, etcd, m, allMembers)
|
||||
case kops.CloudProviderBareMetal:
|
||||
glog.Fatalf("BareMetal not implemented")
|
||||
case kops.CloudProviderOpenstack:
|
||||
err = b.addOpenstackVolume(c, name, volumeSize, zone, etcd, m, allMembers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown cloudprovider %q", b.Cluster.Spec.CloudProvider)
|
||||
}
|
||||
|
|
@ -205,3 +212,33 @@ func (b *MasterVolumeBuilder) addGCEVolume(c *fi.ModelBuilderContext, name strin
|
|||
func (b *MasterVolumeBuilder) addVSphereVolume(c *fi.ModelBuilderContext, name string, volumeSize int32, zone string, etcd *kops.EtcdClusterSpec, m *kops.EtcdMemberSpec, allMembers []string) {
|
||||
fmt.Print("addVSphereVolume to be implemented")
|
||||
}
|
||||
|
||||
func (b *MasterVolumeBuilder) addOpenstackVolume(c *fi.ModelBuilderContext, name string, volumeSize int32, zone string, etcd *kops.EtcdClusterSpec, m *kops.EtcdMemberSpec, allMembers []string) error {
|
||||
volumeType := fi.StringValue(m.VolumeType)
|
||||
if volumeType == "" {
|
||||
return fmt.Errorf("must set ETCDMemberSpec.VolumeType on Openstack platform")
|
||||
}
|
||||
|
||||
// The tags are how protokube knows to mount the volume and use it for etcd
|
||||
tags := make(map[string]string)
|
||||
// Apply all user defined labels on the volumes
|
||||
for k, v := range b.Cluster.Spec.CloudLabels {
|
||||
tags[k] = v
|
||||
}
|
||||
// This is the configuration of the etcd cluster
|
||||
tags[openstack.TagNameEtcdClusterPrefix+etcd.Name] = m.Name + "/" + strings.Join(allMembers, ",")
|
||||
// This says "only mount on a master"
|
||||
tags[openstack.TagNameRolePrefix+"master"] = "1"
|
||||
|
||||
t := &openstacktasks.Volume{
|
||||
Name: s(name),
|
||||
AvailabilityZone: s(zone),
|
||||
VolumeType: s(volumeType),
|
||||
SizeGB: fi.Int64(int64(volumeSize)),
|
||||
Tags: tags,
|
||||
Lifecycle: b.Lifecycle,
|
||||
}
|
||||
c.AddTask(t)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ func addUntaggedRouteTables(cloud awsup.AWSCloud, clusterName string, resources
|
|||
continue
|
||||
}
|
||||
|
||||
t := buildTrackerForRouteTable(rt)
|
||||
t := buildTrackerForRouteTable(rt, clusterName)
|
||||
if resources[t.Type+":"+t.ID] == nil {
|
||||
resources[t.Type+":"+t.ID] = t
|
||||
}
|
||||
|
|
@ -973,19 +973,20 @@ func ListRouteTables(cloud fi.Cloud, clusterName string) ([]*Resource, error) {
|
|||
var resourceTrackers []*Resource
|
||||
|
||||
for _, rt := range routeTables {
|
||||
resourceTracker := buildTrackerForRouteTable(rt)
|
||||
resourceTracker := buildTrackerForRouteTable(rt, clusterName)
|
||||
resourceTrackers = append(resourceTrackers, resourceTracker)
|
||||
}
|
||||
|
||||
return resourceTrackers, nil
|
||||
}
|
||||
|
||||
func buildTrackerForRouteTable(rt *ec2.RouteTable) *Resource {
|
||||
func buildTrackerForRouteTable(rt *ec2.RouteTable, clusterName string) *Resource {
|
||||
resourceTracker := &Resource{
|
||||
Name: FindName(rt.Tags),
|
||||
ID: aws.StringValue(rt.RouteTableId),
|
||||
Type: ec2.ResourceTypeRouteTable,
|
||||
Deleter: DeleteRouteTable,
|
||||
Shared: HasSharedTag(ec2.ResourceTypeRouteTable+":"+*rt.RouteTableId, rt.Tags, clusterName),
|
||||
}
|
||||
|
||||
var blocks []string
|
||||
|
|
|
|||
|
|
@ -88,3 +88,55 @@ func TestAddUntaggedRouteTables(t *testing.T) {
|
|||
t.Fatalf("expected=%q, actual=%q", expected, keys)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListRouteTables(t *testing.T) {
|
||||
cloud := awsup.BuildMockAWSCloud("us-east-1", "abc")
|
||||
//resources := make(map[string]*Resource)
|
||||
clusterName := "me.example.com"
|
||||
ownershipTagKey := "kubernetes.io/cluster/" + clusterName
|
||||
|
||||
c := &mockec2.MockEC2{}
|
||||
cloud.MockEC2 = c
|
||||
|
||||
c.RouteTables = append(c.RouteTables, &ec2.RouteTable{
|
||||
VpcId: aws.String("vpc-1234"),
|
||||
RouteTableId: aws.String("rt-shared"),
|
||||
Tags: []*ec2.Tag{
|
||||
{
|
||||
Key: aws.String("KubernetesCluster"),
|
||||
Value: aws.String(clusterName),
|
||||
},
|
||||
{
|
||||
Key: aws.String(ownershipTagKey),
|
||||
Value: aws.String("shared"),
|
||||
},
|
||||
},
|
||||
})
|
||||
c.RouteTables = append(c.RouteTables, &ec2.RouteTable{
|
||||
VpcId: aws.String("vpc-1234"),
|
||||
RouteTableId: aws.String("rt-owned"),
|
||||
Tags: []*ec2.Tag{
|
||||
{
|
||||
Key: aws.String("KubernetesCluster"),
|
||||
Value: aws.String(clusterName),
|
||||
},
|
||||
{
|
||||
Key: aws.String(ownershipTagKey),
|
||||
Value: aws.String("owned"),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
resources, err := ListRouteTables(cloud, clusterName)
|
||||
if err != nil {
|
||||
t.Fatalf("error listing route tables: %v", err)
|
||||
}
|
||||
for _, rt := range resources {
|
||||
if rt.ID == "rt-shared" && !rt.Shared {
|
||||
t.Fatalf("expected Shared: true, got: %v", rt.Shared)
|
||||
}
|
||||
if rt.ID == "rt-owned" && rt.Shared {
|
||||
t.Fatalf("expected Shared: false, got: %v", rt.Shared)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ go_library(
|
|||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,15 @@ package validation
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"net"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/kops/pkg/apis/kops"
|
||||
"k8s.io/kops/pkg/apis/kops/util"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
|
|
@ -54,6 +58,32 @@ type ValidationNode struct {
|
|||
Status v1.ConditionStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// HasPlaceHolderIP checks if the API DNS has been updated
|
||||
func HasPlaceHolderIP(clusterName string) (bool, error) {
|
||||
|
||||
config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
|
||||
clientcmd.NewDefaultClientConfigLoadingRules(),
|
||||
&clientcmd.ConfigOverrides{CurrentContext: clusterName}).ClientConfig()
|
||||
|
||||
apiAddr, err := url.Parse(config.Host)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("unable to parse Kubernetes cluster API URL: %v", err)
|
||||
}
|
||||
|
||||
hostAddrs, err := net.LookupHost(apiAddr.Host)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("unable to resolve Kubernetes cluster API URL dns: %v", err)
|
||||
}
|
||||
|
||||
for _, h := range hostAddrs {
|
||||
if h == "203.0.113.123" {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// ValidateCluster validate a k8s cluster with a provided instance group list
|
||||
func ValidateCluster(clusterName string, instanceGroupList *kops.InstanceGroupList, clusterKubernetesClient kubernetes.Interface) (*ValidationCluster, error) {
|
||||
var instanceGroups []*kops.InstanceGroup
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ spec:
|
|||
- operator: Exists
|
||||
initContainers:
|
||||
- name: install-cni
|
||||
image: quay.io/coreos/flannel:v0.9.0-amd64
|
||||
image: quay.io/coreos/flannel:v0.9.1-amd64
|
||||
command:
|
||||
- cp
|
||||
args:
|
||||
|
|
@ -113,7 +113,7 @@ spec:
|
|||
mountPath: /etc/kube-flannel/
|
||||
containers:
|
||||
- name: kube-flannel
|
||||
image: quay.io/coreos/flannel:v0.9.0-amd64
|
||||
image: quay.io/coreos/flannel:v0.9.1-amd64
|
||||
command: [ "/opt/bin/flanneld", "--ip-masq", "--kube-subnet-mgr" ]
|
||||
securityContext:
|
||||
privileged: true
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ spec:
|
|||
serviceAccountName: flannel
|
||||
containers:
|
||||
- name: kube-flannel
|
||||
image: quay.io/coreos/flannel:v0.7.1
|
||||
image: quay.io/coreos/flannel:v0.9.1
|
||||
command: [ "/opt/bin/flanneld", "--ip-masq", "--kube-subnet-mgr" ]
|
||||
securityContext:
|
||||
privileged: true
|
||||
|
|
@ -79,7 +79,7 @@ spec:
|
|||
- name: flannel-cfg
|
||||
mountPath: /etc/kube-flannel/
|
||||
- name: install-cni
|
||||
image: quay.io/coreos/flannel:v0.7.1
|
||||
image: quay.io/coreos/flannel:v0.9.1
|
||||
command: [ "/bin/sh", "-c", "set -e -x; cp -f /etc/kube-flannel/cni-conf.json /etc/cni/net.d/10-flannel.conf; while true; do sleep 3600; done" ]
|
||||
resources:
|
||||
limits:
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ spec:
|
|||
# This container runs flannel using the kube-subnet-mgr backend
|
||||
# for allocating subnets.
|
||||
- name: kube-flannel
|
||||
image: quay.io/coreos/flannel:v0.8.0
|
||||
image: quay.io/coreos/flannel:v0.9.1
|
||||
command: [ "/opt/bin/flanneld", "--ip-masq", "--kube-subnet-mgr" ]
|
||||
securityContext:
|
||||
privileged: true
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# This manifest includes the following component versions:
|
||||
# calico/node:v2.6.2
|
||||
# calico/cni:v1.11.0
|
||||
# coreos/flannel:v0.9.0
|
||||
# coreos/flannel:v0.9.1
|
||||
|
||||
# This ConfigMap can be used to configure a self-hosted Canal installation.
|
||||
kind: ConfigMap
|
||||
|
|
@ -194,7 +194,7 @@ spec:
|
|||
# This container runs flannel using the kube-subnet-mgr backend
|
||||
# for allocating subnets.
|
||||
- name: kube-flannel
|
||||
image: quay.io/coreos/flannel:v0.9.0
|
||||
image: quay.io/coreos/flannel:v0.9.1
|
||||
command: [ "/opt/bin/flanneld", "--ip-masq", "--kube-subnet-mgr" ]
|
||||
securityContext:
|
||||
privileged: true
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ spec:
|
|||
# This container runs flannel using the kube-subnet-mgr backend
|
||||
# for allocating subnets.
|
||||
- name: kube-flannel
|
||||
image: quay.io/coreos/flannel:v0.8.0
|
||||
image: quay.io/coreos/flannel:v0.9.1
|
||||
command: [ "/opt/bin/flanneld", "--ip-masq", "--kube-subnet-mgr" ]
|
||||
securityContext:
|
||||
privileged: true
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ metadata:
|
|||
name: calico-config
|
||||
namespace: kube-system
|
||||
data:
|
||||
# The calico-etcd PetSet service IP:port
|
||||
# etcd servers
|
||||
etcd_endpoints: "{{ $cluster := index .EtcdClusters 0 -}}
|
||||
{{- range $j, $member := $cluster.Members -}}
|
||||
{{- if $j }},{{ end -}}
|
||||
|
|
@ -18,33 +18,22 @@ data:
|
|||
# The CNI network configuration to install on each node.
|
||||
cni_network_config: |-
|
||||
{
|
||||
"name": "k8s-pod-network",
|
||||
"cniVersion": "0.3.0",
|
||||
"plugins": [
|
||||
{
|
||||
"type": "calico",
|
||||
"etcd_endpoints": "__ETCD_ENDPOINTS__",
|
||||
"log_level": "info",
|
||||
"ipam": {
|
||||
"name": "k8s-pod-network",
|
||||
"type": "calico",
|
||||
"etcd_endpoints": "__ETCD_ENDPOINTS__",
|
||||
"log_level": "info",
|
||||
"ipam": {
|
||||
"type": "calico-ipam"
|
||||
},
|
||||
"policy": {
|
||||
"type": "k8s",
|
||||
"k8s_api_root": "https://__KUBERNETES_SERVICE_HOST__:__KUBERNETES_SERVICE_PORT__",
|
||||
"k8s_auth_token": "__SERVICEACCOUNT_TOKEN__"
|
||||
},
|
||||
"kubernetes": {
|
||||
"kubeconfig": "/etc/cni/net.d/__KUBECONFIG_FILENAME__"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "portmap",
|
||||
"snat": true,
|
||||
"capabilities": {"portMappings": true}
|
||||
"policy": {
|
||||
"type": "k8s",
|
||||
"k8s_api_root": "https://__KUBERNETES_SERVICE_HOST__:__KUBERNETES_SERVICE_PORT__",
|
||||
"k8s_auth_token": "__SERVICEACCOUNT_TOKEN__"
|
||||
},
|
||||
"kubernetes": {
|
||||
"kubeconfig": "/etc/cni/net.d/__KUBECONFIG_FILENAME__"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
kind: ClusterRole
|
||||
|
|
@ -133,12 +122,15 @@ spec:
|
|||
operator: Exists
|
||||
- effect: NoSchedule
|
||||
operator: Exists
|
||||
# Minimize downtime during a rolling upgrade or deletion; tell Kubernetes to do a "force
|
||||
# deletion": https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods.
|
||||
terminationGracePeriodSeconds: 0
|
||||
containers:
|
||||
# Runs calico/node container on each Kubernetes node. This
|
||||
# container programs network policy and routes on each
|
||||
# host.
|
||||
- name: calico-node
|
||||
image: quay.io/calico/node:v2.4.1
|
||||
image: quay.io/calico/node:v2.6.2
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
|
|
@ -169,6 +161,14 @@ spec:
|
|||
# Auto-detect the BGP IP address.
|
||||
- name: IP
|
||||
value: ""
|
||||
# Disable IPv6 on Kubernetes.
|
||||
- name: FELIX_IPV6SUPPORT
|
||||
value: "false"
|
||||
# Set Felix logging to "info"
|
||||
- name: FELIX_LOGSEVERITYSCREEN
|
||||
value: "info"
|
||||
- name: FELIX_HEALTHENABLED
|
||||
value: "true"
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
|
|
@ -185,7 +185,7 @@ spec:
|
|||
# This container installs the Calico CNI binaries
|
||||
# and CNI network config file on each node.
|
||||
- name: install-cni
|
||||
image: quay.io/calico/cni:v1.10.0
|
||||
image: quay.io/calico/cni:v1.11.0
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
|
|
@ -194,7 +194,7 @@ spec:
|
|||
env:
|
||||
# The name of calico config file
|
||||
- name: CNI_CONF_NAME
|
||||
value: 10-calico.conflist
|
||||
value: 10-calico.conf
|
||||
# The location of the Calico etcd cluster.
|
||||
- name: ETCD_ENDPOINTS
|
||||
valueFrom:
|
||||
|
|
@ -237,8 +237,8 @@ spec:
|
|||
|
||||
---
|
||||
|
||||
# This manifest deploys the Calico policy controller on Kubernetes.
|
||||
# See https://github.com/projectcalico/k8s-policy
|
||||
# This deployment turns off the old "policy-controller". It should remain at 0 replicas, and then
|
||||
# be removed entirely once the new kube-controllers deployment has been deployed above.
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
|
|
@ -246,35 +246,23 @@ metadata:
|
|||
namespace: kube-system
|
||||
labels:
|
||||
k8s-app: calico-policy
|
||||
role.kubernetes.io/networking: "1"
|
||||
spec:
|
||||
# The policy controller can only have a single active instance.
|
||||
replicas: 1
|
||||
# Turn this deployment off in favor of the kube-controllers deployment above.
|
||||
replicas: 0
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
name: calico-policy-controller
|
||||
namespace: kube-system
|
||||
labels:
|
||||
k8s-app: calico-policy-controller
|
||||
role.kubernetes.io/networking: "1"
|
||||
annotations:
|
||||
scheduler.alpha.kubernetes.io/critical-pod: ''
|
||||
k8s-app: calico-policy
|
||||
spec:
|
||||
# The policy controller must run in the host network namespace so that
|
||||
# it isn't governed by policy that would prevent it from working.
|
||||
hostNetwork: true
|
||||
serviceAccountName: calico
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/master
|
||||
effect: NoSchedule
|
||||
- key: CriticalAddonsOnly
|
||||
operator: Exists
|
||||
containers:
|
||||
- name: calico-policy-controller
|
||||
image: quay.io/calico/kube-policy-controller:v0.7.0
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
image: quay.io/calico/kube-controllers:v1.0.0
|
||||
env:
|
||||
# The location of the Calico etcd cluster.
|
||||
- name: ETCD_ENDPOINTS
|
||||
|
|
@ -282,15 +270,6 @@ spec:
|
|||
configMapKeyRef:
|
||||
name: calico-config
|
||||
key: etcd_endpoints
|
||||
# The location of the Kubernetes API. Use the default Kubernetes
|
||||
# service for API access.
|
||||
- name: K8S_API
|
||||
value: "https://kubernetes.default:443"
|
||||
# Since we're running in the host namespace and might not have KubeDNS
|
||||
# access, configure the container's /etc/hosts to resolve
|
||||
# kubernetes.default to the correct service clusterIP.
|
||||
- name: CONFIGURE_ETC_HOSTS
|
||||
value: "true"
|
||||
|
||||
volumeMounts:
|
||||
# Necessary for gossip based DNS
|
||||
|
|
@ -301,6 +280,55 @@ spec:
|
|||
- name: etc-hosts
|
||||
hostPath:
|
||||
path: /etc/hosts
|
||||
---
|
||||
|
||||
# This manifest deploys the Calico Kubernetes controllers.
|
||||
# See https://github.com/projectcalico/kube-controllers
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: calico-kube-controllers
|
||||
namespace: kube-system
|
||||
labels:
|
||||
k8s-app: calico-kube-controllers
|
||||
role.kubernetes.io/networking: "1"
|
||||
spec:
|
||||
# The controllers can only have a single active instance.
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
name: calico-kube-controllers
|
||||
namespace: kube-system
|
||||
labels:
|
||||
k8s-app: calico-kube-controllers
|
||||
role.kubernetes.io/networking: "1"
|
||||
annotations:
|
||||
scheduler.alpha.kubernetes.io/critical-pod: ''
|
||||
spec:
|
||||
# The controllers must run in the host network namespace so that
|
||||
# it isn't governed by policy that would prevent it from working.
|
||||
hostNetwork: true
|
||||
serviceAccountName: calico
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/master
|
||||
effect: NoSchedule
|
||||
- key: CriticalAddonsOnly
|
||||
operator: Exists
|
||||
containers:
|
||||
- name: calico-kube-controllers
|
||||
image: quay.io/calico/kube-controllers:v1.0.0
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
env:
|
||||
# The location of the Calico etcd cluster.
|
||||
- name: ETCD_ENDPOINTS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: calico-config
|
||||
key: etcd_endpoints
|
||||
|
||||
|
||||
|
||||
{{ if and (eq .CloudProvider "aws") (.Networking.Calico.CrossSubnet) -}}
|
||||
# This manifest installs the k8s-ec2-srcdst container, which disables
|
||||
|
|
|
|||
|
|
@ -16,11 +16,10 @@ data:
|
|||
calico_backend: "bird"
|
||||
|
||||
# The CNI network configuration to install on each node.
|
||||
# cniVersion should be 0.1.0 on k8s: https://github.com/projectcalico/calico/issues/742
|
||||
cni_network_config: |-
|
||||
{
|
||||
"name": "k8s-pod-network",
|
||||
"cniVersion": "0.1.0",
|
||||
"cniVersion": "0.3.0",
|
||||
"plugins": [
|
||||
{
|
||||
"type": "calico",
|
||||
|
|
@ -137,7 +137,7 @@ spec:
|
|||
effect: NoSchedule
|
||||
containers:
|
||||
- name: romana-daemon
|
||||
image: quay.io/romana/daemon:v2.0-preview.2
|
||||
image: quay.io/romana/daemon:v2.0.0
|
||||
imagePullPolicy: Always
|
||||
resources:
|
||||
requests:
|
||||
|
|
@ -170,7 +170,7 @@ spec:
|
|||
effect: NoSchedule
|
||||
containers:
|
||||
- name: romana-listener
|
||||
image: quay.io/romana/listener:v2.0-preview.2
|
||||
image: quay.io/romana/listener:v2.0.0
|
||||
imagePullPolicy: Always
|
||||
resources:
|
||||
requests:
|
||||
|
|
@ -185,6 +185,8 @@ metadata:
|
|||
name: romana-agent
|
||||
namespace: kube-system
|
||||
spec:
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
|
|
@ -200,7 +202,7 @@ spec:
|
|||
effect: NoSchedule
|
||||
containers:
|
||||
- name: romana-agent
|
||||
image: quay.io/romana/agent:v2.0-preview.2
|
||||
image: quay.io/romana/agent:v2.0.0
|
||||
imagePullPolicy: Always
|
||||
resources:
|
||||
requests:
|
||||
|
|
@ -213,6 +215,10 @@ spec:
|
|||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
- name: NODEIP
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.hostIP
|
||||
args:
|
||||
- --service-cluster-ip-range={{ .ServiceClusterIPRange }}
|
||||
securityContext:
|
||||
|
|
@ -299,7 +305,7 @@ spec:
|
|||
effect: NoSchedule
|
||||
containers:
|
||||
- name: romana-aws
|
||||
image: quay.io/romana/aws:v2.0-preview.2
|
||||
image: quay.io/romana/aws:v2.0.0
|
||||
imagePullPolicy: Always
|
||||
resources:
|
||||
requests:
|
||||
|
|
@ -328,7 +334,7 @@ spec:
|
|||
effect: NoSchedule
|
||||
containers:
|
||||
- name: romana-vpcrouter
|
||||
image: quay.io/romana/vpcrouter-romana-plugin
|
||||
image: quay.io/romana/vpcrouter-romana-plugin:1.1.12
|
||||
imagePullPolicy: Always
|
||||
resources:
|
||||
requests:
|
||||
|
|
@ -185,6 +185,14 @@ func NewAWSCloud(region string, tags map[string]string) (AWSCloud, error) {
|
|||
config = config.WithCredentialsChainVerboseErrors(true)
|
||||
config = request.WithRetryer(config, newLoggingRetryer(ClientMaxRetries))
|
||||
|
||||
// We have the updated aws sdk from 1.9, but don't have https://github.com/kubernetes/kubernetes/pull/55307
|
||||
// Set the SleepDelay function to work around this
|
||||
// TODO: Remove once we update to k8s >= 1.9 (or a version of the retry delayer than includes this)
|
||||
config.SleepDelay = func(d time.Duration) {
|
||||
glog.V(6).Infof("aws request sleeping for %v", d)
|
||||
time.Sleep(d)
|
||||
}
|
||||
|
||||
requestLogger := newRequestLogger(2)
|
||||
|
||||
sess, err := session.NewSession(config)
|
||||
|
|
|
|||
|
|
@ -133,8 +133,11 @@ func (c *MockAWSCloud) BuildTags(name *string) map[string]string {
|
|||
}
|
||||
|
||||
func (c *MockAWSCloud) Tags() map[string]string {
|
||||
glog.Fatalf("MockAWSCloud Tags not implemented")
|
||||
return nil
|
||||
tags := make(map[string]string)
|
||||
for k, v := range c.tags {
|
||||
tags[k] = v
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
func (c *MockAWSCloud) CreateTags(resourceId string, tags map[string]string) error {
|
||||
|
|
|
|||
|
|
@ -468,11 +468,10 @@ func (b *BootstrapChannelBuilder) buildManifest() (*channelsapi.Addons, map[stri
|
|||
|
||||
if b.cluster.Spec.Networking.Calico != nil {
|
||||
key := "networking.projectcalico.org"
|
||||
// 2.6.3-kops.1 = 2.6.2 with kops manifest tweaks. This should go away with the next version bump.
|
||||
versions := map[string]string{
|
||||
"pre-k8s-1.6": "2.4.1",
|
||||
"k8s-1.6": "2.4.2-kops.1",
|
||||
"k8s-1.8": "2.6.3-kops.1",
|
||||
"k8s-1.6": "2.6.2",
|
||||
"k8s-1.7": "2.6.2",
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -499,14 +498,14 @@ func (b *BootstrapChannelBuilder) buildManifest() (*channelsapi.Addons, map[stri
|
|||
Version: fi.String(versions[id]),
|
||||
Selector: networkingSelector,
|
||||
Manifest: fi.String(location),
|
||||
KubernetesVersion: ">=1.6.0 <1.8.0",
|
||||
KubernetesVersion: ">=1.6.0 <1.7.0",
|
||||
Id: id,
|
||||
})
|
||||
manifests[key+"-"+id] = "addons/" + location
|
||||
}
|
||||
|
||||
{
|
||||
id := "k8s-1.8"
|
||||
id := "k8s-1.7"
|
||||
location := key + "/" + id + ".yaml"
|
||||
|
||||
addons.Spec.Addons = append(addons.Spec.Addons, &channelsapi.AddonSpec{
|
||||
|
|
@ -514,7 +513,7 @@ func (b *BootstrapChannelBuilder) buildManifest() (*channelsapi.Addons, map[stri
|
|||
Version: fi.String(versions[id]),
|
||||
Selector: networkingSelector,
|
||||
Manifest: fi.String(location),
|
||||
KubernetesVersion: ">=1.8.0",
|
||||
KubernetesVersion: ">=1.7.0",
|
||||
Id: id,
|
||||
})
|
||||
manifests[key+"-"+id] = "addons/" + location
|
||||
|
|
@ -598,18 +597,18 @@ func (b *BootstrapChannelBuilder) buildManifest() (*channelsapi.Addons, map[stri
|
|||
|
||||
if b.cluster.Spec.Networking.Romana != nil {
|
||||
key := "networking.romana"
|
||||
version := "v2.0-preview.3"
|
||||
version := "v2.0.0"
|
||||
|
||||
{
|
||||
location := key + "/k8s-1.6.yaml"
|
||||
id := "k8s-1.6"
|
||||
location := key + "/k8s-1.7.yaml"
|
||||
id := "k8s-1.7"
|
||||
|
||||
addons.Spec.Addons = append(addons.Spec.Addons, &channelsapi.AddonSpec{
|
||||
Name: fi.String(key),
|
||||
Version: fi.String(version),
|
||||
Selector: networkingSelector,
|
||||
Manifest: fi.String(location),
|
||||
KubernetesVersion: ">=1.6.0",
|
||||
KubernetesVersion: ">=1.7.0",
|
||||
Id: id,
|
||||
})
|
||||
manifests[key+"-"+id] = "addons/" + location
|
||||
|
|
|
|||
|
|
@ -12,7 +12,13 @@ go_library(
|
|||
"//pkg/apis/kops:go_default_library",
|
||||
"//pkg/cloudinstances:go_default_library",
|
||||
"//upup/pkg/fi:go_default_library",
|
||||
"//util/pkg/vfs:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/gophercloud/gophercloud:go_default_library",
|
||||
"//vendor/github.com/gophercloud/gophercloud/openstack:go_default_library",
|
||||
"//vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/kubernetes/federation/pkg/dnsprovider:go_default_library",
|
||||
],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -18,25 +18,90 @@ package openstack
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/gophercloud/gophercloud"
|
||||
os "github.com/gophercloud/gophercloud/openstack"
|
||||
cinder "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/kops/pkg/apis/kops"
|
||||
"k8s.io/kops/pkg/cloudinstances"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/util/pkg/vfs"
|
||||
"k8s.io/kubernetes/federation/pkg/dnsprovider"
|
||||
)
|
||||
|
||||
const TagNameEtcdClusterPrefix = "k8s.io/etcd/"
|
||||
const TagNameRolePrefix = "k8s.io/role/"
|
||||
const TagClusterName = "KubernetesCluster"
|
||||
|
||||
// readBackoff is the backoff strategy for openstack read retries.
|
||||
var readBackoff = wait.Backoff{
|
||||
Duration: time.Second,
|
||||
Factor: 1.5,
|
||||
Jitter: 0.1,
|
||||
Steps: 4,
|
||||
}
|
||||
|
||||
// writeBackoff is the backoff strategy for openstack write retries.
|
||||
var writeBackoff = wait.Backoff{
|
||||
Duration: time.Second,
|
||||
Factor: 1.5,
|
||||
Jitter: 0.1,
|
||||
Steps: 5,
|
||||
}
|
||||
|
||||
type OpenstackCloud interface {
|
||||
fi.Cloud
|
||||
|
||||
// SetVolumeTags will set the tags for the Cinder volume
|
||||
SetVolumeTags(id string, tags map[string]string) error
|
||||
|
||||
// GetCloudTags will return the tags attached on cloud
|
||||
GetCloudTags() map[string]string
|
||||
|
||||
// ListVolumes will return the Cinder volumes which match the options
|
||||
ListVolumes(opt cinder.ListOpts) ([]cinder.Volume, error)
|
||||
|
||||
// CreateVolume will create a new Cinder Volume
|
||||
CreateVolume(opt cinder.CreateOpts) (*cinder.Volume, error)
|
||||
}
|
||||
|
||||
type openstackCloud struct {
|
||||
cinderClient *gophercloud.ServiceClient
|
||||
tags map[string]string
|
||||
}
|
||||
|
||||
var _ fi.Cloud = &openstackCloud{}
|
||||
|
||||
func NewOpenstackCloud() (OpenstackCloud, error) {
|
||||
return &openstackCloud{}, nil
|
||||
func NewOpenstackCloud(tags map[string]string) (OpenstackCloud, error) {
|
||||
config := vfs.OpenstackConfig{}
|
||||
|
||||
authOption, err := config.GetCredential()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
provider, err := os.AuthenticatedClient(authOption)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error building openstack authenticated client: %v", err)
|
||||
}
|
||||
|
||||
endpointOpt, err := config.GetServiceConfig("Cinder")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cinderClient, err := os.NewBlockStorageV2(provider, endpointOpt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error building swift client: %v", err)
|
||||
}
|
||||
|
||||
c := &openstackCloud{
|
||||
cinderClient: cinderClient,
|
||||
tags: tags,
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *openstackCloud) ProviderID() kops.CloudProviderID {
|
||||
|
|
@ -62,3 +127,78 @@ func (c *openstackCloud) DeleteGroup(g *cloudinstances.CloudInstanceGroup) error
|
|||
func (c *openstackCloud) GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodes []v1.Node) (map[string]*cloudinstances.CloudInstanceGroup, error) {
|
||||
return nil, fmt.Errorf("openstackCloud::GetCloudGroups not implemented")
|
||||
}
|
||||
|
||||
func (c *openstackCloud) SetVolumeTags(id string, tags map[string]string) error {
|
||||
if len(tags) == 0 {
|
||||
return nil
|
||||
}
|
||||
if id == "" {
|
||||
return fmt.Errorf("error setting tags to unknown volume")
|
||||
}
|
||||
glog.V(4).Infof("setting tags to cinder volume %q: %v", id, tags)
|
||||
|
||||
opt := cinder.UpdateOpts{Metadata: tags}
|
||||
done, err := vfs.RetryWithBackoff(writeBackoff, func() (bool, error) {
|
||||
_, err := cinder.Update(c.cinderClient, id, opt).Extract()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error setting tags to cinder volume %q: %v", id, err)
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
} else if done {
|
||||
return nil
|
||||
} else {
|
||||
return wait.ErrWaitTimeout
|
||||
}
|
||||
}
|
||||
|
||||
func (c *openstackCloud) GetCloudTags() map[string]string {
|
||||
return c.tags
|
||||
}
|
||||
|
||||
func (c *openstackCloud) ListVolumes(opt cinder.ListOpts) ([]cinder.Volume, error) {
|
||||
var volumes []cinder.Volume
|
||||
|
||||
done, err := vfs.RetryWithBackoff(readBackoff, func() (bool, error) {
|
||||
allPages, err := cinder.List(c.cinderClient, opt).AllPages()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error listing volumes %v: %v", opt, err)
|
||||
}
|
||||
|
||||
vs, err := cinder.ExtractVolumes(allPages)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error extracting volumes from pages: %v", err)
|
||||
}
|
||||
volumes = vs
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
return volumes, err
|
||||
} else if done {
|
||||
return volumes, nil
|
||||
} else {
|
||||
return volumes, wait.ErrWaitTimeout
|
||||
}
|
||||
}
|
||||
|
||||
func (c *openstackCloud) CreateVolume(opt cinder.CreateOpts) (*cinder.Volume, error) {
|
||||
var volume *cinder.Volume
|
||||
|
||||
done, err := vfs.RetryWithBackoff(writeBackoff, func() (bool, error) {
|
||||
v, err := cinder.Create(c.cinderClient, opt).Extract()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error creating volume %v: %v", opt, err)
|
||||
}
|
||||
volume = v
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
return volume, err
|
||||
} else if done {
|
||||
return volume, nil
|
||||
} else {
|
||||
return volume, wait.ErrWaitTimeout
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["volume.go"],
|
||||
importpath = "k8s.io/kops/upup/pkg/fi/cloudup/openstacktasks",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//upup/pkg/fi:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/openstack:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes:go_default_library",
|
||||
],
|
||||
)
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
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 openstacktasks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
cinder "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
|
||||
)
|
||||
|
||||
type Volume struct {
|
||||
ID *string
|
||||
Name *string
|
||||
AvailabilityZone *string
|
||||
VolumeType *string
|
||||
SizeGB *int64
|
||||
Tags map[string]string
|
||||
Lifecycle *fi.Lifecycle
|
||||
}
|
||||
|
||||
var _ fi.CompareWithID = &Volume{}
|
||||
|
||||
func (c *Volume) CompareWithID() *string {
|
||||
return c.ID
|
||||
}
|
||||
|
||||
func (c *Volume) Find(context *fi.Context) (*Volume, error) {
|
||||
cloud := context.Cloud.(openstack.OpenstackCloud)
|
||||
opt := cinder.ListOpts{
|
||||
Name: fi.StringValue(c.Name),
|
||||
Metadata: cloud.GetCloudTags(),
|
||||
}
|
||||
volumes, err := cloud.ListVolumes(opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n := len(volumes)
|
||||
if n == 0 {
|
||||
return nil, nil
|
||||
} else if n != 1 {
|
||||
return nil, fmt.Errorf("found multiple Volumes with name: %s", fi.StringValue(c.Name))
|
||||
}
|
||||
v := volumes[0]
|
||||
actual := &Volume{
|
||||
ID: fi.String(v.ID),
|
||||
Name: fi.String(v.Name),
|
||||
AvailabilityZone: fi.String(v.AvailabilityZone),
|
||||
VolumeType: fi.String(v.VolumeType),
|
||||
SizeGB: fi.Int64(int64(v.Size)),
|
||||
Tags: v.Metadata,
|
||||
Lifecycle: c.Lifecycle,
|
||||
}
|
||||
return actual, nil
|
||||
}
|
||||
|
||||
func (c *Volume) Run(context *fi.Context) error {
|
||||
cloud := context.Cloud.(openstack.OpenstackCloud)
|
||||
for k, v := range cloud.GetCloudTags() {
|
||||
c.Tags[k] = v
|
||||
}
|
||||
|
||||
return fi.DefaultDeltaRunMethod(c, context)
|
||||
}
|
||||
|
||||
func (_ *Volume) CheckChanges(a, e, changes *Volume) error {
|
||||
if a == nil {
|
||||
if e.Name == nil {
|
||||
return fi.RequiredField("Name")
|
||||
}
|
||||
if e.AvailabilityZone == nil {
|
||||
return fi.RequiredField("AvailabilityZone")
|
||||
}
|
||||
if e.VolumeType == nil {
|
||||
return fi.RequiredField("VolumeType")
|
||||
}
|
||||
if e.SizeGB == nil {
|
||||
return fi.RequiredField("SizeGB")
|
||||
}
|
||||
} else {
|
||||
if changes.ID != nil {
|
||||
return fi.CannotChangeField("ID")
|
||||
}
|
||||
if changes.AvailabilityZone != nil {
|
||||
return fi.CannotChangeField("AvailabilityZone")
|
||||
}
|
||||
if changes.VolumeType != nil {
|
||||
return fi.CannotChangeField("VolumeType")
|
||||
}
|
||||
if changes.SizeGB != nil {
|
||||
return fi.CannotChangeField("SizeGB")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Volume) RenderOpenstack(t *openstack.OpenstackAPITarget, a, e, changes *Volume) error {
|
||||
if a == nil {
|
||||
glog.V(2).Infof("Creating PersistentVolume with Name:%q", fi.StringValue(e.Name))
|
||||
|
||||
opt := cinder.CreateOpts{
|
||||
Size: int(*e.SizeGB),
|
||||
AvailabilityZone: fi.StringValue(e.AvailabilityZone),
|
||||
Metadata: e.Tags,
|
||||
Name: fi.StringValue(e.Name),
|
||||
VolumeType: fi.StringValue(e.VolumeType),
|
||||
}
|
||||
|
||||
v, err := t.Cloud.CreateVolume(opt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating PersistentVolume: %v", err)
|
||||
}
|
||||
|
||||
e.ID = fi.String(v.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
if changes != nil && changes.Tags != nil {
|
||||
glog.V(2).Infof("Update the tags on volume %q: %v, the differences are %v", fi.StringValue(e.ID), e.Tags, changes.Tags)
|
||||
|
||||
err := t.Cloud.SetVolumeTags(fi.StringValue(e.ID), e.Tags)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error updating the tags on volume %q: %v", fi.StringValue(e.ID), err)
|
||||
}
|
||||
}
|
||||
|
||||
glog.V(2).Infof("Openstack task Volume::RenderOpenstack did nothing")
|
||||
return nil
|
||||
}
|
||||
|
|
@ -133,7 +133,8 @@ func BuildCloud(cluster *kops.Cluster) (fi.Cloud, error) {
|
|||
}
|
||||
case kops.CloudProviderOpenstack:
|
||||
{
|
||||
osc, err := openstack.NewOpenstackCloud()
|
||||
cloudTags := map[string]string{openstack.TagClusterName: cluster.ObjectMeta.Name}
|
||||
osc, err := openstack.NewOpenstackCloud(cloudTags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,8 +32,10 @@ type SecretStore interface {
|
|||
DeleteSecret(item *KeystoreItem) error
|
||||
// FindSecret finds a secret, if exists. Returns nil,nil if not found
|
||||
FindSecret(id string) (*Secret, error)
|
||||
// GetOrCreateSecret creates or replace a secret
|
||||
// GetOrCreateSecret creates a secret
|
||||
GetOrCreateSecret(id string, secret *Secret) (current *Secret, created bool, err error)
|
||||
// ReplaceSecret will forcefully update an existing secret if it exists
|
||||
ReplaceSecret(id string, secret *Secret) (current *Secret, err error)
|
||||
// ListSecrets lists the ids of all known secrets
|
||||
ListSecrets() ([]string, error)
|
||||
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ func (c *ClientsetSecretStore) GetOrCreateSecret(name string, secret *fi.Secret)
|
|||
return s, false, nil
|
||||
}
|
||||
|
||||
_, err = c.createSecret(secret, name)
|
||||
_, err = c.createSecret(secret, name, false)
|
||||
if err != nil {
|
||||
if errors.IsAlreadyExists(err) && i == 0 {
|
||||
glog.Infof("Got already-exists error when writing secret; likely due to concurrent creation. Will retry")
|
||||
|
|
@ -181,6 +181,21 @@ func (c *ClientsetSecretStore) GetOrCreateSecret(name string, secret *fi.Secret)
|
|||
return s, true, nil
|
||||
}
|
||||
|
||||
// ReplaceSecret implements fi.SecretStore::ReplaceSecret
|
||||
func (c *ClientsetSecretStore) ReplaceSecret(name string, secret *fi.Secret) (*fi.Secret, error) {
|
||||
_, err := c.createSecret(secret, name, true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to write secret: %v", err)
|
||||
}
|
||||
|
||||
// Confirm the secret exists
|
||||
s, err := c.loadSecret(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to load secret immmediately after creation: %v", err)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// loadSecret returns the named secret, if it exists, otherwise returns nil
|
||||
func (c *ClientsetSecretStore) loadSecret(name string) (*fi.Secret, error) {
|
||||
name = NamePrefix + name
|
||||
|
|
@ -207,8 +222,8 @@ func parseSecret(keyset *kops.Keyset) (*fi.Secret, error) {
|
|||
return s, nil
|
||||
}
|
||||
|
||||
// createSecret writes the secret, but only if it does not exist
|
||||
func (c *ClientsetSecretStore) createSecret(s *fi.Secret, name string) (*kops.Keyset, error) {
|
||||
// createSecret will create the Secret, overwriting an existing secret if replace is true
|
||||
func (c *ClientsetSecretStore) createSecret(s *fi.Secret, name string, replace bool) (*kops.Keyset, error) {
|
||||
keyset := &kops.Keyset{}
|
||||
keyset.Name = NamePrefix + name
|
||||
keyset.Spec.Type = kops.SecretTypeSecret
|
||||
|
|
@ -221,5 +236,8 @@ func (c *ClientsetSecretStore) createSecret(s *fi.Secret, name string) (*kops.Ke
|
|||
PrivateMaterial: s.Data,
|
||||
})
|
||||
|
||||
if replace {
|
||||
return c.clientset.Keysets(c.namespace).Update(keyset)
|
||||
}
|
||||
return c.clientset.Keysets(c.namespace).Create(keyset)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ func (c *VFSSecretStore) GetOrCreateSecret(id string, secret *fi.Secret) (*fi.Se
|
|||
return nil, false, err
|
||||
}
|
||||
|
||||
err = c.createSecret(secret, p, acl)
|
||||
err = c.createSecret(secret, p, acl, false)
|
||||
if err != nil {
|
||||
if os.IsExist(err) && i == 0 {
|
||||
glog.Infof("Got already-exists error when writing secret; likely due to concurrent creation. Will retry")
|
||||
|
|
@ -151,6 +151,27 @@ func (c *VFSSecretStore) GetOrCreateSecret(id string, secret *fi.Secret) (*fi.Se
|
|||
return s, true, nil
|
||||
}
|
||||
|
||||
func (c *VFSSecretStore) ReplaceSecret(id string, secret *fi.Secret) (*fi.Secret, error) {
|
||||
p := c.buildSecretPath(id)
|
||||
|
||||
acl, err := acls.GetACL(p, c.cluster)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = c.createSecret(secret, p, acl, true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to write secret: %v", err)
|
||||
}
|
||||
|
||||
// Confirm the secret exists
|
||||
s, err := c.loadSecret(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to load secret immmediately after creation %v: %v", p, err)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (c *VFSSecretStore) loadSecret(p vfs.Path) (*fi.Secret, error) {
|
||||
data, err := p.ReadFile()
|
||||
if err != nil {
|
||||
|
|
@ -166,11 +187,15 @@ func (c *VFSSecretStore) loadSecret(p vfs.Path) (*fi.Secret, error) {
|
|||
return s, nil
|
||||
}
|
||||
|
||||
// createSecret writes the secret, but only if it does not exists
|
||||
func (c *VFSSecretStore) createSecret(s *fi.Secret, p vfs.Path, acl vfs.ACL) error {
|
||||
// createSecret will create the Secret, overwriting an existing secret if replace is true
|
||||
func (c *VFSSecretStore) createSecret(s *fi.Secret, p vfs.Path, acl vfs.ACL, replace bool) error {
|
||||
data, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error serializing secret: %v", err)
|
||||
}
|
||||
|
||||
if replace {
|
||||
return p.WriteFile(data, acl)
|
||||
}
|
||||
return p.CreateFile(data, acl)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue