Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fleet: add wait command, code refactor & update tests #5327

Merged
merged 48 commits into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
d1bab98
initial: azdev extension creation
Jul 26, 2022
54c14c0
update min cli supported version
Jul 26, 2022
7d1d359
add fleet client sdk
Jul 26, 2022
b90034c
all fleet commands with help and params
Jul 28, 2022
7e00779
update git code owners
Jul 28, 2022
f9c99d4
update git code owners
Jul 28, 2022
8482100
nit: remove redundancy
Aug 11, 2022
26c4be9
add live and unit tests
Aug 11, 2022
c534881
file cleanup
Aug 11, 2022
57c83f8
file cleanup
Aug 11, 2022
bea74bb
file cleanup
Aug 11, 2022
2191b34
file cleanup
Aug 11, 2022
d14e92e
file cleanup
Aug 11, 2022
ca07417
add fleet list credentials command
Aug 15, 2022
326cfa9
Merge pull request #6 from pdaru/fleet-extension
pdaru Aug 15, 2022
6eb1b32
update live test
Aug 15, 2022
d683af5
Merge pull request #7 from pdaru/fleet-extension
pdaru Aug 15, 2022
14fcd29
file clean up
Aug 15, 2022
ce4a579
Merge pull request #8 from pdaru/fleet-extension
pdaru Aug 15, 2022
57f5858
add GET,PATCH commands & update tests
Aug 19, 2022
9dd68b2
Merge pull request #9 from pdaru/fleet-extension
pdaru Aug 19, 2022
4271e5c
update style
Aug 19, 2022
12ff9b0
Merge pull request #10 from pdaru/fleet-extension
pdaru Aug 19, 2022
817f253
add licence
Aug 19, 2022
6d0ad33
Merge pull request #11 from pdaru/fleet-extension
pdaru Aug 19, 2022
9c6013c
add licence
Aug 19, 2022
4f43721
fix checks
Aug 19, 2022
5c7c1f3
add fleet to service_name
Aug 19, 2022
82e0f3f
update owner
Aug 19, 2022
b939dca
update test recording
Aug 19, 2022
4a28a64
update test
Aug 19, 2022
45b29e9
check fix
Aug 22, 2022
52e6993
update recording
Aug 23, 2022
1fcd3b7
update command names as per convention
Aug 23, 2022
b27b45b
update tests
Aug 23, 2022
bcd9ccf
update tests
Aug 23, 2022
69e0c87
update setup url
pdaru Aug 23, 2022
b820ec0
update setup python ver
pdaru Aug 23, 2022
2d4e839
fix bugs
Aug 23, 2022
a8ab5cd
Merge branch 'Azure:main' into main
pdaru Sep 1, 2022
7345ce2
code refactor, add wait command & update tests
Sep 8, 2022
6bc15ab
fix style
Sep 8, 2022
e58e7fe
Merge branch 'Azure:main' into main
pdaru Sep 8, 2022
ecc5377
fix test file
Sep 8, 2022
8269723
fix test file
Sep 8, 2022
2aeb170
fix test file
Sep 8, 2022
0b5ac25
fix test file
Sep 8, 2022
6646c9b
update min version
Sep 8, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
add fleet list credentials command
  • Loading branch information
Pankil Daru committed Aug 15, 2022
commit ca0741722173fb95c751cb61a3687584b742a312
9 changes: 9 additions & 0 deletions src/fleet/azext_fleet/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@
short-summary: Delete an existing fleet.
"""

helps['fleet credentials list'] = """
type: command
short-summary: List fleet kubeconfig credentials
parameters:
- name: --overwrite-existing
type: bool
short-summary: Overwrite any existing cluster entry with the same name.
"""

helps['fleet member'] = """
type: group
short-summary: Commands to manage a fleet member.
Expand Down
135 changes: 135 additions & 0 deletions src/fleet/azext_fleet/_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import errno
import os
import platform
import stat
import tempfile
import yaml

from knack.log import get_logger
from knack.prompting import NoTTYException, prompt_y_n
from knack.util import CLIError
from knack.log import get_logger

logger = get_logger(__name__)

def print_or_merge_credentials(path, kubeconfig, overwrite_existing, context_name):
"""Merge an unencrypted kubeconfig into the file at the specified path, or print it to
stdout if the path is "-".
"""
# Special case for printing to stdout
if path == "-":
print(kubeconfig)
return

# ensure that at least an empty ~/.kube/config exists
directory = os.path.dirname(path)
if directory and not os.path.exists(directory):
try:
os.makedirs(directory)
except OSError as ex:
if ex.errno != errno.EEXIST:
raise
if not os.path.exists(path):
with os.fdopen(os.open(path, os.O_CREAT | os.O_WRONLY, 0o600), 'wt'):
pass

# merge the new kubeconfig into the existing one
fd, temp_path = tempfile.mkstemp()
additional_file = os.fdopen(fd, 'w+t')
try:
additional_file.write(kubeconfig)
additional_file.flush()
_merge_kubernetes_configurations(
path, temp_path, overwrite_existing, context_name)
except yaml.YAMLError as ex:
logger.warning(
'Failed to merge credentials to kube config file: %s', ex)
finally:
additional_file.close()
os.remove(temp_path)

def _merge_kubernetes_configurations(existing_file, addition_file, replace, context_name=None):
existing = _load_kubernetes_configuration(existing_file)
addition = _load_kubernetes_configuration(addition_file)

if context_name is not None:
addition['contexts'][0]['name'] = context_name
addition['contexts'][0]['context']['cluster'] = context_name
addition['clusters'][0]['name'] = context_name
addition['current-context'] = context_name

# rename the admin context so it doesn't overwrite the user context
for ctx in addition.get('contexts', []):
try:
if ctx['context']['user'].startswith('clusterAdmin'):
admin_name = ctx['name'] + '-admin'
addition['current-context'] = ctx['name'] = admin_name
break
except (KeyError, TypeError):
continue

if addition is None:
raise CLIError(
'failed to load additional configuration from {}'.format(addition_file))

if existing is None:
existing = addition
else:
_handle_merge(existing, addition, 'clusters', replace)
_handle_merge(existing, addition, 'users', replace)
_handle_merge(existing, addition, 'contexts', replace)
existing['current-context'] = addition['current-context']

# check that ~/.kube/config is only read- and writable by its owner
if platform.system() != "Windows" and not os.path.islink(existing_file):
existing_file_perms = "{:o}".format(stat.S_IMODE(os.lstat(existing_file).st_mode))
if not existing_file_perms.endswith("600"):
logger.warning(
'%s has permissions "%s".\nIt should be readable and writable only by its owner.',
existing_file,
existing_file_perms,
)

with open(existing_file, 'w+') as stream:
yaml.safe_dump(existing, stream, default_flow_style=False)

current_context = addition.get('current-context', 'UNKNOWN')
msg = 'Merged "{}" as current context in {}'.format(
current_context, existing_file)
print(msg)

def _handle_merge(existing, addition, key, replace):
if not addition[key]:
return
if existing[key] is None:
existing[key] = addition[key]
return

for i in addition[key]:
for j in existing[key]:
if i['name'] == j['name']:
if replace or i == j:
existing[key].remove(j)
else:
msg = 'A different object named {} already exists in your kubeconfig file.\nOverwrite?'
overwrite = False
try:
overwrite = prompt_y_n(msg.format(i['name']))
except NoTTYException:
pass
if overwrite:
existing[key].remove(j)
else:
msg = 'A different object named {} already exists in {} in your kubeconfig file.'
raise CLIError(msg.format(i['name'], key))
existing[key].append(i)

def _load_kubernetes_configuration(filename):
try:
with open(filename) as stream:
return yaml.safe_load(stream)
except (IOError, OSError) as ex:
if getattr(ex, 'errno', 0) == errno.ENOENT:
raise CLIError('{} does not exist'.format(filename))
except (yaml.parser.ParserError, UnicodeDecodeError) as ex:
raise CLIError('Error parsing {} ({})'.format(filename, str(ex)))
13 changes: 10 additions & 3 deletions src/fleet/azext_fleet/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,31 @@
# --------------------------------------------------------------------------------------------
# pylint: disable=line-too-long

import os
from argcomplete.completers import FilesCompleter
from azure.cli.core.commands.parameters import (
get_resource_name_completion_list,
tags_type
tags_type,
file_type
)
from azext_fleet._validators import validate_member_cluster_id


def load_arguments(self, _):

with self.argument_context('fleet') as c:
c.argument('name', options_list=['--name', '-n'], help='Specify the fleet name.', completer=get_resource_name_completion_list('Microsoft.ContainerService/ManagedClusters'))
c.argument('name', options_list=['--name', '-n'], help='Specify the fleet name.')

with self.argument_context('fleet create') as c:
c.argument('tags', tags_type)
c.argument('dns_name_prefix', options_list=['--dns-name-prefix', '-p'])

with self.argument_context('fleet credentials list') as c:
c.argument('context_name', options_list=['--context'], help='If specified, overwrite the default context name.')
c.argument('path', options_list=['--file', '-f'], type=file_type, completer=FilesCompleter(), default=os.path.join(os.path.expanduser('~'), '.kube', 'config'))

with self.argument_context('fleet member') as c:
c.argument('member_name', help='Specify a member name. Default value is name of the managed cluster.')
c.argument('member_name', help='Specify a member name. Default value is name of the managed cluster.', completer=get_resource_name_completion_list('Microsoft.ContainerService/ManagedClusters'))

with self.argument_context('fleet member join') as c:
c.argument('member_cluster_id', validator=validate_member_cluster_id)
1 change: 1 addition & 0 deletions src/fleet/azext_fleet/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def load_command_table(self, _):
with self.command_group("fleet", fleets_sdk, client_factory=cf_fleets) as g:
g.custom_command("create", "create_fleet", supports_no_wait=True)
g.custom_command("delete", "delete_fleet", supports_no_wait=True)
g.custom_command("credentials list", "list_credentials")

# fleet members command group
with self.command_group("fleet member", fleet_members_sdk, client_factory=cf_fleet_members) as g:
Expand Down
26 changes: 25 additions & 1 deletion src/fleet/azext_fleet/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

import os

from knack.log import get_logger
from knack.util import CLIError

from azure.cli.core.util import sdk_no_wait

from azext_fleet._client_factory import CUSTOM_MGMT_FLEET
from azext_fleet._resourcegroup import get_rg_location
from azext_fleet._helpers import print_or_merge_credentials


logger = get_logger(__name__)
Expand Down Expand Up @@ -56,14 +60,34 @@ def delete_fleet(cmd, # pylint: disable=unused-argument
return sdk_no_wait(no_wait, client.begin_delete, resource_group_name, name)


def list_credentials(cmd, # pylint: disable=unused-argument
client,
resource_group_name,
name,
path=os.path.join(os.path.expanduser(
'~'), '.kube', 'config'),
overwrite_existing=False,
context_name=None):
credentialResults = client.list_credentials(resource_group_name, name)
if not credentialResults:
raise CLIError("No Kubernetes credentials found.")

try:
kubeconfig = credentialResults.kubeconfigs[0].value.decode(
encoding='UTF-8')
print_or_merge_credentials(
path, kubeconfig, overwrite_existing, context_name)
except (IndexError, ValueError):
raise CLIError("Fail to find kubeconfig file.")


def join_fleet_member(cmd,
client,
resource_group_name,
name,
member_cluster_id,
member_name=None,
no_wait=False):
logger.info('in create fleetmember')
FleetMember = cmd.get_models(
"FleetMember",
resource_type=CUSTOM_MGMT_FLEET,
Expand Down
2 changes: 2 additions & 0 deletions src/fleet/azext_fleet/tests/latest/test_fleet_scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ def test_fleet(self):
self.check('name', '{name}')
])

self.cmd('fleet credentials list -g {rg} -n {name} --overwrite-existing')

mc_id = self.cmd('aks create -g {rg} -n {member_name}', checks=[
self.check('name', '{member_name}')
]).get_output_in_json()['id']
Expand Down