Allow specification of ssh authorized key and token url for coreos.

1. Implements token generation mechanism for coreos.
2. Add cluster_type and discovery_token_url fields in magnum.conf.
3. Add field to bay model to specify the ssh authorized key.
   This value is passed into the kubenode-coreos.yaml
   through kubecluster-coreos.yaml.
   This key is used to login into the coreos instance.

Implements part of bp introduce-coreos

Change-Id: Id8b18a7bf016120102c8da1c3339f70564aedae4
This commit is contained in:
digambar
2015-03-13 17:12:51 +05:30
parent 361d0551fb
commit 094ddf3b47
9 changed files with 158 additions and 6 deletions

View File

@@ -514,6 +514,16 @@
# Location of template to build a k8s cluster. (string value)
#template_path = /etc/magnum/templates/heat-kubernetes/kubecluster.yaml
# Define the Cluster type. (string value)
# Whenever you want to create cluster of any of these
# fedora-atomic,coreos, ironic then specify a cluster_type.
#cluster_type = fedora-atomic, coreos, ironic
# Discovery token url for coreos cluster. (string value)
# Forming a coreos cluster will require a discovery token if your cloud
# has internet support otherwise uuid will generate the token for you.
#discovery_token_url = https://discovery.etcd.io/new
# Number of attempts to query the Heat stack for finding out the
# status of the created stack and getting url of the DU created in the
# stack (integer value)

View File

@@ -73,6 +73,9 @@ class BayModel(base.APIBase):
docker_volume_size = wtypes.IntegerType()
"""The size in GB of the docker volume"""
ssh_authorized_key = wtypes.text
"""The SSH Authorized Key"""
links = wsme.wsattr([link.Link], readonly=True)
"""A list containing a self link and associated baymodel links"""
@@ -118,6 +121,7 @@ class BayModel(base.APIBase):
fixed_network='private',
apiserver_port=8080,
docker_volume_size=25,
ssh_authorized_key='ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAB',
created_at=datetime.datetime.utcnow(),
updated_at=datetime.datetime.utcnow())
return cls._convert_with_links(sample, 'http://localhost:9511', expand)

View File

@@ -12,6 +12,9 @@
# License for the specific language governing permissions and limitations
# under the License.
import requests
import uuid
from heatclient.common import template_utils
from heatclient import exc
from oslo_config import cfg
@@ -34,6 +37,12 @@ k8s_heat_opts = [
'kubecluster.yaml'),
help=_(
'Location of template to build a k8s cluster. ')),
cfg.StrOpt('cluster_type',
default=None,
help=_('Cluster types are fedora-atomic, coreos, ironic.')),
cfg.StrOpt('discovery_token_url',
default=None,
help=_('coreos discovery token url.')),
cfg.IntOpt('max_attempts',
default=2000,
help=('Number of attempts to query the Heat stack for '
@@ -51,12 +60,27 @@ cfg.CONF.register_opts(k8s_heat_opts, group='k8s_heat')
LOG = logging.getLogger(__name__)
def _get_coreos_token(context):
if cfg.CONF.k8s_heat.cluster_type == 'coreos':
token = ""
discovery_url = cfg.CONF.k8s_heat.discovery_token_url
if discovery_url:
coreos_token_url = requests.get(discovery_url)
token = str(coreos_token_url.text.split('/')[3])
else:
token = uuid.uuid4().hex
return token
else:
return None
def _extract_bay_definition(context, bay):
baymodel = objects.BayModel.get_by_uuid(context, bay.baymodel_id)
token = _get_coreos_token(context)
bay_definition = {
'ssh_key_name': baymodel.keypair_id,
'external_network_id': baymodel.external_network_id,
'token': token,
}
if baymodel.dns_nameserver:
bay_definition['dns_nameserver'] = baymodel.dns_nameserver
@@ -75,6 +99,8 @@ def _extract_bay_definition(context, bay):
bay_definition['docker_volume_size'] = baymodel.docker_volume_size
if baymodel.fixed_network:
bay_definition['fixed_network'] = baymodel.fixed_network
if baymodel.ssh_authorized_key:
bay_definition['ssh_authorized_key'] = baymodel.ssh_authorized_key
return bay_definition

View File

@@ -0,0 +1,38 @@
# Copyright 2015 OpenStack Foundation
# 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.
"""ssh authorized key
Revision ID: 2d1354bbf76e
Revises: 1afee1db6cd0
Create Date: 2015-03-13 14:05:58.744652
"""
# revision identifiers, used by Alembic.
revision = '2d1354bbf76e'
down_revision = '1afee1db6cd0'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column('baymodel', sa.Column('ssh_authorized_key',
sa.Text, nullable=True))
def downgrade():
op.drop_column('baymodel', 'ssh_authorized_key')

View File

@@ -27,6 +27,7 @@ from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Integer
from sqlalchemy import schema
from sqlalchemy import String
from sqlalchemy import Text
from sqlalchemy.types import TypeDecorator, TEXT
from magnum.common import paths
@@ -152,6 +153,7 @@ class BayModel(Base):
dns_nameserver = Column(String(255))
apiserver_port = Column(Integer())
docker_volume_size = Column(Integer())
ssh_authorized_key = Column(Text)
class Container(Base):

View File

@@ -41,6 +41,7 @@ class BayModel(base.MagnumObject):
'fixed_network': obj_utils.str_or_none,
'apiserver_port': obj_utils.int_or_none,
'docker_volume_size': obj_utils.int_or_none,
'ssh_authorized_key': obj_utils.str_or_none,
}
@staticmethod

View File

@@ -53,6 +53,7 @@ class TestListBayModel(api_base.FunctionalTest):
self.assertNotIn('external_network_id', response['baymodels'][0])
self.assertNotIn('fixed_network', response['baymodels'][0])
self.assertNotIn('docker_volume_size', response['baymodels'][0])
self.assertNotIn('ssh_authorized_key', response['baymodels'][0])
def test_get_one(self):
baymodel = obj_utils.create_test_baymodel(self.context)
@@ -65,6 +66,7 @@ class TestListBayModel(api_base.FunctionalTest):
self.assertIn('external_network_id', response)
self.assertIn('fixed_network', response)
self.assertIn('docker_volume_size', response)
self.assertIn('ssh_authorized_key', response)
def test_detail(self):
baymodel = obj_utils.create_test_baymodel(self.context)
@@ -77,6 +79,7 @@ class TestListBayModel(api_base.FunctionalTest):
self.assertIn('external_network_id', response['baymodels'][0])
self.assertIn('fixed_network', response['baymodels'][0])
self.assertIn('docker_volume_size', response['baymodels'][0])
self.assertIn('ssh_authorized_key', response['baymodels'][0])
def test_detail_against_single(self):
baymodel = obj_utils.create_test_baymodel(self.context)
@@ -133,11 +136,19 @@ class TestPatch(api_base.FunctionalTest):
def setUp(self):
super(TestPatch, self).setUp()
self.baymodel = obj_utils.create_test_baymodel(self.context,
name='bay_model_example_A',
image_id='nerdherd',
apiserver_port=8080,
fixed_network='private',
docker_volume_size=20)
name='bay_model_example_A',
image_id='nerdherd',
apiserver_port=8080,
fixed_network='private',
docker_volume_size=20,
ssh_authorized_key='ssh-rsa AAAAB3NzaC1ycEAAAADA'
'v0XRqg3tm+jlsOKGO81lPDH+KaSJ'
'Q7wvmjUqszP/H6NC/m+qiGp/sTis'
'DYucqbeuM7nmJi+8Hb55y1xWoOZI'
'KMa71G5/4EOQxuQ/sgW965OOO2Hq'
'X8vjlQUnTK0HijrbSTLxp/9kazWW'
'FrfsdB8RtZBN digambar@magnum'
)
def test_update_not_found(self):
uuid = utils.generate_uuid()
@@ -176,6 +187,8 @@ class TestPatch(api_base.FunctionalTest):
response['fixed_network'])
self.assertEqual(self.baymodel.docker_volume_size,
response['docker_volume_size'])
self.assertEqual(self.baymodel.ssh_authorized_key,
response['ssh_authorized_key'])
def test_remove_singular(self):
baymodel = obj_utils.create_test_baymodel(self.context,
@@ -198,6 +211,8 @@ class TestPatch(api_base.FunctionalTest):
response['fixed_network'])
self.assertEqual(self.baymodel.docker_volume_size,
response['docker_volume_size'])
self.assertEqual(self.baymodel.ssh_authorized_key,
response['ssh_authorized_key'])
def test_remove_non_existent_property_fail(self):
response = self.patch_json('/baymodels/%s' % self.baymodel.uuid,
@@ -260,6 +275,8 @@ class TestPatch(api_base.FunctionalTest):
response['fixed_network'])
self.assertEqual(self.baymodel.docker_volume_size,
response['docker_volume_size'])
self.assertEqual(self.baymodel.ssh_authorized_key,
response['ssh_authorized_key'])
def test_remove_uuid(self):
response = self.patch_json('/baymodels/%s' % self.baymodel.uuid,

View File

@@ -32,6 +32,8 @@ class TestBayK8sHeat(base.TestCase):
'external_network_id': 'external_network_id',
'fixed_network': 'private',
'docker_volume_size': 20,
'ssh_authorized_key': 'ssh_authorized_key',
'token': None,
}
self.bay_dict = {
'baymodel_id': 'xx-xx-xx-xx',
@@ -62,6 +64,8 @@ class TestBayK8sHeat(base.TestCase):
'number_of_minions': '1',
'fixed_network': 'private',
'docker_volume_size': 20,
'ssh_authorized_key': 'ssh_authorized_key',
'token': None,
}
self.assertEqual(expected, bay_definition)
@@ -86,6 +90,8 @@ class TestBayK8sHeat(base.TestCase):
'number_of_minions': '1',
'fixed_network': 'private',
'docker_volume_size': 20,
'ssh_authorized_key': 'ssh_authorized_key',
'token': None,
}
self.assertEqual(expected, bay_definition)
@@ -110,6 +116,8 @@ class TestBayK8sHeat(base.TestCase):
'number_of_minions': '1',
'fixed_network': 'private',
'docker_volume_size': 20,
'ssh_authorized_key': 'ssh_authorized_key',
'token': None,
}
self.assertEqual(expected, bay_definition)
@@ -134,6 +142,8 @@ class TestBayK8sHeat(base.TestCase):
'number_of_minions': '1',
'fixed_network': 'private',
'docker_volume_size': 20,
'ssh_authorized_key': 'ssh_authorized_key',
'token': None,
}
self.assertEqual(expected, bay_definition)
@@ -158,6 +168,8 @@ class TestBayK8sHeat(base.TestCase):
'fixed_network': 'private',
'master_flavor': 'master_flavor_id',
'number_of_minions': '1',
'ssh_authorized_key': 'ssh_authorized_key',
'token': None,
}
self.assertEqual(expected, bay_definition)
@@ -182,6 +194,8 @@ class TestBayK8sHeat(base.TestCase):
'server_flavor': 'flavor_id',
'number_of_minions': '1',
'docker_volume_size': 20,
'ssh_authorized_key': 'ssh_authorized_key',
'token': None,
}
self.assertEqual(expected, bay_definition)
@@ -206,6 +220,34 @@ class TestBayK8sHeat(base.TestCase):
'number_of_minions': '1',
'fixed_network': 'private',
'docker_volume_size': 20,
'ssh_authorized_key': 'ssh_authorized_key',
'token': None,
}
self.assertEqual(expected, bay_definition)
@patch('magnum.objects.BayModel.get_by_uuid')
def test_extract_bay_definition_without_ssh_authorized_key(self,
mock_objects_baymodel_get_by_uuid):
baymodel_dict = self.baymodel_dict
baymodel_dict['ssh_authorized_key'] = None
baymodel = objects.BayModel(self.context, **baymodel_dict)
mock_objects_baymodel_get_by_uuid.return_value = baymodel
bay = objects.Bay(self.context, **self.bay_dict)
bay_definition = bay_k8s_heat._extract_bay_definition(self.context,
bay)
expected = {
'ssh_key_name': 'keypair_id',
'external_network_id': 'external_network_id',
'dns_nameserver': 'dns_nameserver',
'server_image': 'image_id',
'master_flavor': 'master_flavor_id',
'server_flavor': 'flavor_id',
'number_of_minions': '1',
'fixed_network': 'private',
'docker_volume_size': 20,
'token': None,
}
self.assertEqual(expected, bay_definition)
@@ -231,6 +273,8 @@ class TestBayK8sHeat(base.TestCase):
'number_of_minions': '1',
'fixed_network': 'private',
'docker_volume_size': 20,
'ssh_authorized_key': 'ssh_authorized_key',
'token': None,
}
self.assertEqual(expected, bay_definition)
@@ -255,6 +299,8 @@ class TestBayK8sHeat(base.TestCase):
'fixed_network': 'private',
'master_flavor': 'master_flavor_id',
'docker_volume_size': 20,
'ssh_authorized_key': 'ssh_authorized_key',
'token': None,
}
self.assertEqual(expected, bay_definition)

View File

@@ -35,6 +35,14 @@ def get_test_baymodel(**kw):
'dns_nameserver': kw.get('dns_nameserver', '8.8.1.1'),
'apiserver_port': kw.get('apiserver_port', 8080),
'docker_volume_size': kw.get('docker_volume_size', 20),
'ssh_authorized_key': kw.get('ssh_authorized_key',
'ssh-rsa AAAAB3NzaC1ycEAAAADA'
'v0XRqg3tm+jlsOKGO81lPDH+KaSJ'
'Q7wvmjUqszP/H6NC/m+qiGp/sTis'
'DYucqbeuM7nmJi+8Hb55y1xWoOZI'
'KMa71G5/4EOQxuQ/sgW965OOO2Hq'
'X8vjlQUnTK0HijrbSTLxp/9kazWW'
'FrfsdB8RtZBN digambar@magnum'),
'created_at': kw.get('created_at'),
'updated_at': kw.get('updated_at'),
}