def configure_apiserver():
    api_opts = {}

    # Get the tls paths from the layer data.
    layer_options = layer.options('tls-client')
    ca_cert_path = layer_options.get('ca_certificate_path')
    client_cert_path = layer_options.get('client_certificate_path')
    client_key_path = layer_options.get('client_key_path')
    server_cert_path = layer_options.get('server_certificate_path')
    server_key_path = layer_options.get('server_key_path')

    if is_privileged():
        api_opts['allow-privileged'] = 'true'
        set_state('kubernetes-master.privileged')
    else:
        api_opts['allow-privileged'] = 'false'
        remove_state('kubernetes-master.privileged')

    # Handle static options for now
    api_opts['service-cluster-ip-range'] = service_cidr()
    api_opts['min-request-timeout'] = '300'
    api_opts['v'] = '4'
    api_opts['tls-cert-file'] = server_cert_path
    api_opts['tls-private-key-file'] = server_key_path
    api_opts['kubelet-certificate-authority'] = ca_cert_path
    api_opts['kubelet-client-certificate'] = client_cert_path
    api_opts['kubelet-client-key'] = client_key_path
    api_opts['logtostderr'] = 'true'
    api_opts['insecure-bind-address'] = '127.0.0.1'
    api_opts['insecure-port'] = '8080'
    api_opts['storage-backend'] = 'etcd2'  # FIXME: add etcd3 support

    admission_control = [
        'Initializers', 'NamespaceLifecycle', 'LimitRanger', 'ServiceAccount',
        'ResourceQuota', 'DefaultTolerationSeconds'
    ]

    auth_mode = hookenv.config('authorization-mode')
    if 'Node' in auth_mode:
        admission_control.append('NodeRestriction')

    api_opts['authorization-mode'] = auth_mode

    if get_version('kube-apiserver') < (1, 6):
        hookenv.log('Removing DefaultTolerationSeconds from admission-control')
        admission_control.remove('DefaultTolerationSeconds')
    if get_version('kube-apiserver') < (1, 7):
        hookenv.log('Removing Initializers from admission-control')
        admission_control.remove('Initializers')
    api_opts['admission-control'] = ','.join(admission_control)

    configure_kubernetes_service('kube-apiserver', api_opts, 'api-extra-args')

    set_state('kube-apiserver.do-restart')
Exemple #2
0
def enable_gpu():
    """Enable GPU usage on this node.

    """
    if get_version('kubelet') < (1, 9):
        hookenv.status_set(
            'active',
            'Upgrade to snap channel >= 1.9/stable to enable GPU suppport.')
        return

    hookenv.log('Enabling gpu mode')
    try:
        # Not sure why this is necessary, but if you don't run this, k8s will
        # think that the node has 0 gpus (as shown by the output of
        # `kubectl get nodes -o yaml`
        check_call(['nvidia-smi'])
    except CalledProcessError as cpe:
        hookenv.log('Unable to communicate with the NVIDIA driver.')
        hookenv.log(cpe)
        return

    set_label('gpu', 'true')
    set_label('cuda', 'true')

    set_state('kubernetes-worker.gpu.enabled')
    set_state('kubernetes-worker.restart-needed')
Exemple #3
0
def enable_gpu():
    """Enable GPU usage on this node.

    """
    config = hookenv.config()
    if config['allow-privileged'] == "false":
        hookenv.status_set(
            'active', 'GPUs available. Set allow-privileged="auto" to enable.')
        return

    hookenv.log('Enabling gpu mode')
    try:
        # Not sure why this is necessary, but if you don't run this, k8s will
        # think that the node has 0 gpus (as shown by the output of
        # `kubectl get nodes -o yaml`
        check_call(['nvidia-smi'])
    except CalledProcessError as cpe:
        hookenv.log('Unable to communicate with the NVIDIA driver.')
        hookenv.log(cpe)
        return

    kubelet_opts = FlagManager('kubelet')
    if get_version('kubelet') < (1, 6):
        hookenv.log('Adding --experimental-nvidia-gpus=1 to kubelet')
        kubelet_opts.add('experimental-nvidia-gpus', '1')
    else:
        hookenv.log('Adding --feature-gates=Accelerators=true to kubelet')
        kubelet_opts.add('feature-gates', 'Accelerators=true')

    # Apply node labels
    _apply_node_label('gpu=true', overwrite=True)
    _apply_node_label('cuda=true', overwrite=True)

    set_state('kubernetes-worker.gpu.enabled')
    set_state('kubernetes-worker.restart-needed')
def configure_cdk_addons():
    ''' Configure CDK addons '''
    remove_state('cdk-addons.configured')
    load_gpu_plugin = hookenv.config('enable-nvidia-plugin').lower()
    gpuEnable = (get_version('kube-apiserver') >= (1, 9) and
                 load_gpu_plugin == "auto" and
                 is_state('kubernetes-master.gpu.enabled'))
    dbEnabled = str(hookenv.config('enable-dashboard-addons')).lower()
    dnsEnabled = str(hookenv.config('enable-kube-dns')).lower()
    metricsEnabled = str(hookenv.config('enable-metrics')).lower()
    args = [
        'arch=' + arch(),
        'dns-ip=' + get_deprecated_dns_ip(),
        'dns-domain=' + hookenv.config('dns_domain'),
        'enable-dashboard=' + dbEnabled,
        'enable-kube-dns=' + dnsEnabled,
        'enable-metrics=' + metricsEnabled,
        'enable-gpu=' + str(gpuEnable).lower()
    ]
    check_call(['snap', 'set', 'cdk-addons'] + args)
    if not addons_ready():
        remove_state('cdk-addons.configured')
        return

    set_state('cdk-addons.configured')
def gpu_with_no_privileged():
    """We were in gpu mode, but the operator has set allow-privileged="false",
    so we can't run in gpu mode anymore.

    """
    if get_version('kube-apiserver') < (1, 9):
        remove_state('kubernetes-master.gpu.enabled')
Exemple #6
0
def configure_kubelet(dns):
    layer_options = layer.options('tls-client')
    ca_cert_path = layer_options.get('ca_certificate_path')
    server_cert_path = layer_options.get('server_certificate_path')
    server_key_path = layer_options.get('server_key_path')

    kubelet_opts = {}
    kubelet_opts['require-kubeconfig'] = 'true'
    kubelet_opts['kubeconfig'] = kubeconfig_path
    kubelet_opts['network-plugin'] = 'cni'
    kubelet_opts['v'] = '0'
    kubelet_opts['address'] = '0.0.0.0'
    kubelet_opts['port'] = '10250'
    kubelet_opts['cluster-dns'] = dns['sdn-ip']
    kubelet_opts['cluster-domain'] = dns['domain']
    kubelet_opts['anonymous-auth'] = 'false'
    kubelet_opts['client-ca-file'] = ca_cert_path
    kubelet_opts['tls-cert-file'] = server_cert_path
    kubelet_opts['tls-private-key-file'] = server_key_path
    kubelet_opts['logtostderr'] = 'true'
    kubelet_opts['fail-swap-on'] = 'false'

    privileged = is_state('kubernetes-worker.privileged')
    kubelet_opts['allow-privileged'] = 'true' if privileged else 'false'

    if is_state('kubernetes-worker.gpu.enabled'):
        if get_version('kubelet') < (1, 6):
            hookenv.log('Adding --experimental-nvidia-gpus=1 to kubelet')
            kubelet_opts['experimental-nvidia-gpus'] = '1'
        else:
            hookenv.log('Adding --feature-gates=Accelerators=true to kubelet')
            kubelet_opts['feature-gates'] = 'Accelerators=true'

    configure_kubernetes_service('kubelet', kubelet_opts, 'kubelet-extra-args')
def set_cloud_pending():
    k8s_version = get_version('kubelet')
    k8s_1_11 = k8s_version >= (1, 11)
    k8s_1_12 = k8s_version >= (1, 12)
    vsphere_joined = is_state('endpoint.vsphere.joined')
    azure_joined = is_state('endpoint.azure.joined')
    if (vsphere_joined and not k8s_1_12) or (azure_joined and not k8s_1_11):
        set_state('kubernetes-worker.cloud.blocked')
    else:
        remove_state('kubernetes-worker.cloud.blocked')
    set_state('kubernetes-worker.cloud.pending')
def set_privileged():
    """Update the allow-privileged flag for kubelet.

    """
    privileged = hookenv.config('allow-privileged').lower()
    gpu_needs_privileged = (is_state('kubernetes-worker.gpu.enabled') and
                            get_version('kubelet') < (1, 9))

    if privileged == 'auto':
        privileged = 'true' if gpu_needs_privileged else 'false'

    if privileged == 'false' and gpu_needs_privileged:
        disable_gpu()
        remove_state('kubernetes-worker.gpu.enabled')
def on_gpu_available(kube_control):
    """The remote side (kubernetes-worker) is gpu-enabled.

    We need to run in privileged mode.

    """
    kube_version = get_version('kube-apiserver')
    config = hookenv.config()
    if (config['allow-privileged'].lower() == "false" and
            kube_version < (1, 9)):
        return

    remove_state('kubernetes-master.components.started')
    set_state('kubernetes-master.gpu.enabled')
Exemple #10
0
def set_privileged():
    """Return 'true' if privileged containers are needed.
    This is when a) the user requested them
                 b) user does not care (auto) and GPUs are available in a pre
                    1.9 era
    """
    privileged = hookenv.config('allow-privileged').lower()
    gpu_needs_privileged = (is_state('kubernetes-worker.gpu.enabled')
                            and get_version('kubelet') < (1, 9))

    if privileged == 'auto':
        privileged = 'true' if gpu_needs_privileged else 'false'

    if privileged == 'false' and gpu_needs_privileged:
        disable_gpu()
        remove_state('kubernetes-worker.gpu.enabled')
        # No need to restart kubernetes (set the restart-needed state)
        # because set-privileged is already in the restart path

    return privileged
Exemple #11
0
def disable_gpu():
    """Disable GPU usage on this node.

    This handler fires when we're running in gpu mode, and then the operator
    sets allow-privileged="false". Since we can no longer run privileged
    containers, we need to disable gpu mode.

    """
    hookenv.log('Disabling gpu mode')

    kubelet_opts = FlagManager('kubelet')
    if get_version('kubelet') < (1, 6):
        kubelet_opts.destroy('experimental-nvidia-gpus')
    else:
        kubelet_opts.remove('feature-gates', 'Accelerators=true')

    # Remove node labels
    _apply_node_label('gpu', delete=True)
    _apply_node_label('cuda', delete=True)

    remove_state('kubernetes-worker.gpu.enabled')
    set_state('kubernetes-worker.restart-needed')
def enable_gpu():
    """Enable GPU usage on this node.

    """
    config = hookenv.config()
    if config['allow-privileged'] == "false":
        hookenv.status_set(
            'active',
            'GPUs available. Set allow-privileged="auto" to enable.'
        )
        return

    hookenv.log('Enabling gpu mode')

    kubelet_opts = FlagManager('kubelet')
    if get_version('kubelet') < (1, 6):
        hookenv.log('Adding --experimental-nvidia-gpus=1 to kubelet')
        kubelet_opts.add('--experimental-nvidia-gpus', '1')
    else:
        hookenv.log('Adding --feature-gates=Accelerators=true to kubelet')
        kubelet_opts.add('--feature-gates', 'Accelerators=true')

    # enable privileged mode and re-render config files
    set_privileged("true", render_config=False)
    render_init_scripts()

    # Apply node labels
    _apply_node_label('gpu=true', overwrite=True)
    _apply_node_label('cuda=true', overwrite=True)

    # Not sure why this is necessary, but if you don't run this, k8s will
    # think that the node has 0 gpus (as shown by the output of
    # `kubectl get nodes -o yaml`
    check_call(['nvidia-smi'])

    set_state('kubernetes-worker.gpu.enabled')
    set_state('kubernetes-worker.kubelet.restart')
Exemple #13
0
def launch_default_ingress_controller():
    ''' Launch the Kubernetes ingress controller & default backend (404) '''
    config = hookenv.config()

    # need to test this in case we get in
    # here from a config change to the image
    if not config.get('ingress'):
        return

    context = {}
    context['arch'] = arch()
    addon_path = '/root/cdk/addons/{}'

    context['defaultbackend_image'] = config.get('default-backend-image')
    if (context['defaultbackend_image'] == ""
            or context['defaultbackend_image'] == "auto"):
        if context['arch'] == 's390x':
            context['defaultbackend_image'] = \
                "k8s.gcr.io/defaultbackend-s390x:1.4"
        elif context['arch'] == 'arm64':
            context['defaultbackend_image'] = \
                "k8s.gcr.io/defaultbackend-arm64:1.4"
        else:
            context['defaultbackend_image'] = \
                "k8s.gcr.io/defaultbackend:1.4"

    # Render the default http backend (404) replicationcontroller manifest
    manifest = addon_path.format('default-http-backend.yaml')
    render('default-http-backend.yaml', manifest, context)
    hookenv.log('Creating the default http backend.')
    try:
        kubectl('apply', '-f', manifest)
    except CalledProcessError as e:
        hookenv.log(e)
        hookenv.log(
            'Failed to create default-http-backend. Will attempt again next update.'
        )  # noqa
        hookenv.close_port(80)
        hookenv.close_port(443)
        return

    # Render the ingress daemon set controller manifest
    context['ingress_image'] = config.get('nginx-image')
    if context['ingress_image'] == "" or context['ingress_image'] == "auto":
        images = {
            'amd64':
            'quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.15.0',  # noqa
            'arm64':
            'quay.io/kubernetes-ingress-controller/nginx-ingress-controller-arm64:0.15.0',  # noqa
            's390x':
            'quay.io/kubernetes-ingress-controller/nginx-ingress-controller-s390x:0.15.0',  # noqa
            'ppc64el':
            'quay.io/kubernetes-ingress-controller/nginx-ingress-controller-ppc64le:0.15.0',  # noqa
        }
        context['ingress_image'] = images.get(context['arch'], images['amd64'])
    if get_version('kubelet') < (1, 9):
        context['daemonset_api_version'] = 'extensions/v1beta1'
    else:
        context['daemonset_api_version'] = 'apps/v1beta2'
    context['juju_application'] = hookenv.service_name()
    manifest = addon_path.format('ingress-daemon-set.yaml')
    render('ingress-daemon-set.yaml', manifest, context)
    hookenv.log('Creating the ingress daemon set.')
    try:
        kubectl('apply', '-f', manifest)
    except CalledProcessError as e:
        hookenv.log(e)
        hookenv.log(
            'Failed to create ingress controller. Will attempt again next update.'
        )  # noqa
        hookenv.close_port(80)
        hookenv.close_port(443)
        return

    set_state('kubernetes-worker.ingress.available')
    hookenv.open_port(80)
    hookenv.open_port(443)
def configure_apiserver(etcd_connection_string, leader_etcd_version):
    api_opts = {}

    # Get the tls paths from the layer data.
    layer_options = layer.options('tls-client')
    ca_cert_path = layer_options.get('ca_certificate_path')
    client_cert_path = layer_options.get('client_certificate_path')
    client_key_path = layer_options.get('client_key_path')
    server_cert_path = layer_options.get('server_certificate_path')
    server_key_path = layer_options.get('server_key_path')

    # at one point in time, this code would set ca-client-cert,
    # but this was removed. This was before configure_kubernetes_service
    # kept track of old arguments and removed them, so client-ca-cert
    # was able to hang around forever stored in the snap configuration.
    # This removes that stale configuration from the snap if it still
    # exists.
    api_opts['client-ca-file'] = 'null'

    if is_privileged():
        api_opts['allow-privileged'] = 'true'
        set_state('kubernetes-master.privileged')
    else:
        api_opts['allow-privileged'] = 'false'
        remove_state('kubernetes-master.privileged')

    # Handle static options for now
    api_opts['service-cluster-ip-range'] = service_cidr()
    api_opts['min-request-timeout'] = '300'
    api_opts['v'] = '4'
    api_opts['tls-cert-file'] = server_cert_path
    api_opts['tls-private-key-file'] = server_key_path
    api_opts['kubelet-certificate-authority'] = ca_cert_path
    api_opts['kubelet-client-certificate'] = client_cert_path
    api_opts['kubelet-client-key'] = client_key_path
    api_opts['logtostderr'] = 'true'
    api_opts['insecure-bind-address'] = '127.0.0.1'
    api_opts['insecure-port'] = '8080'
    api_opts['storage-backend'] = leader_etcd_version
    api_opts['basic-auth-file'] = '/root/cdk/basic_auth.csv'
    api_opts['token-auth-file'] = '/root/cdk/known_tokens.csv'
    api_opts['service-account-key-file'] = '/root/cdk/serviceaccount.key'
    api_opts['kubelet-preferred-address-types'] = \
        '[InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP]'
    api_opts['advertise-address'] = get_ingress_address('kube-control')

    etcd_dir = '/root/cdk/etcd'
    etcd_ca = os.path.join(etcd_dir, 'client-ca.pem')
    etcd_key = os.path.join(etcd_dir, 'client-key.pem')
    etcd_cert = os.path.join(etcd_dir, 'client-cert.pem')

    api_opts['etcd-cafile'] = etcd_ca
    api_opts['etcd-keyfile'] = etcd_key
    api_opts['etcd-certfile'] = etcd_cert
    api_opts['etcd-servers'] = etcd_connection_string

    admission_control_pre_1_9 = [
        'Initializers', 'NamespaceLifecycle', 'LimitRanger', 'ServiceAccount',
        'ResourceQuota', 'DefaultTolerationSeconds'
    ]

    admission_control = [
        'NamespaceLifecycle', 'LimitRanger', 'ServiceAccount',
        'PersistentVolumeLabel', 'DefaultStorageClass',
        'DefaultTolerationSeconds', 'MutatingAdmissionWebhook',
        'ValidatingAdmissionWebhook', 'ResourceQuota'
    ]

    auth_mode = hookenv.config('authorization-mode')
    if 'Node' in auth_mode:
        admission_control.append('NodeRestriction')

    api_opts['authorization-mode'] = auth_mode

    kube_version = get_version('kube-apiserver')
    if kube_version < (1, 6):
        hookenv.log('Removing DefaultTolerationSeconds from admission-control')
        admission_control_pre_1_9.remove('DefaultTolerationSeconds')
    if kube_version < (1, 7):
        hookenv.log('Removing Initializers from admission-control')
        admission_control_pre_1_9.remove('Initializers')
    if kube_version < (1, 9):
        api_opts['admission-control'] = ','.join(admission_control_pre_1_9)
    else:
        api_opts['admission-control'] = ','.join(admission_control)

    if kube_version > (1, 6) and \
       hookenv.config('enable-metrics'):
        api_opts['requestheader-client-ca-file'] = ca_cert_path
        api_opts['requestheader-allowed-names'] = 'client'
        api_opts['requestheader-extra-headers-prefix'] = 'X-Remote-Extra-'
        api_opts['requestheader-group-headers'] = 'X-Remote-Group'
        api_opts['requestheader-username-headers'] = 'X-Remote-User'
        api_opts['proxy-client-cert-file'] = client_cert_path
        api_opts['proxy-client-key-file'] = client_key_path
        api_opts['enable-aggregator-routing'] = 'true'
        api_opts['client-ca-file'] = ca_cert_path

    configure_kubernetes_service('kube-apiserver', api_opts, 'api-extra-args')
    restart_apiserver()
Exemple #15
0
def configure_master_services():
    ''' Add remaining flags for the master services and configure snaps to use
    them '''

    api_opts = FlagManager('kube-apiserver')
    controller_opts = FlagManager('kube-controller-manager')
    scheduler_opts = FlagManager('kube-scheduler')
    scheduler_opts.add('v', '2')

    # Get the tls paths from the layer data.
    layer_options = layer.options('tls-client')
    ca_cert_path = layer_options.get('ca_certificate_path')
    client_cert_path = layer_options.get('client_certificate_path')
    client_key_path = layer_options.get('client_key_path')
    server_cert_path = layer_options.get('server_certificate_path')
    server_key_path = layer_options.get('server_key_path')

    if is_privileged():
        api_opts.add('allow-privileged', 'true', strict=True)
        set_state('kubernetes-master.privileged')
    else:
        api_opts.add('allow-privileged', 'false', strict=True)
        remove_state('kubernetes-master.privileged')

    # Handle static options for now
    api_opts.add('service-cluster-ip-range', service_cidr())
    api_opts.add('min-request-timeout', '300')
    api_opts.add('v', '4')
    api_opts.add('client-ca-file', ca_cert_path)
    api_opts.add('tls-cert-file', server_cert_path)
    api_opts.add('tls-private-key-file', server_key_path)
    api_opts.add('kubelet-certificate-authority', ca_cert_path)
    api_opts.add('kubelet-client-certificate', client_cert_path)
    api_opts.add('kubelet-client-key', client_key_path)
    api_opts.add('logtostderr', 'true')
    api_opts.add('insecure-bind-address', '127.0.0.1')
    api_opts.add('insecure-port', '8080')
    api_opts.add('storage-backend', 'etcd2')  # FIXME: add etcd3 support
    admission_control = [
        'NamespaceLifecycle', 'LimitRanger', 'ServiceAccount', 'ResourceQuota',
        'DefaultTolerationSeconds'
    ]

    if get_version('kube-apiserver') < (1, 6):
        hookenv.log('Removing DefaultTolerationSeconds from admission-control')
        admission_control.remove('DefaultTolerationSeconds')
    api_opts.add('admission-control', ','.join(admission_control), strict=True)

    # Default to 3 minute resync. TODO: Make this configureable?
    controller_opts.add('min-resync-period', '3m')
    controller_opts.add('v', '2')
    controller_opts.add('root-ca-file', ca_cert_path)
    controller_opts.add('logtostderr', 'true')
    controller_opts.add('master', 'http://127.0.0.1:8080')

    scheduler_opts.add('v', '2')
    scheduler_opts.add('logtostderr', 'true')
    scheduler_opts.add('master', 'http://127.0.0.1:8080')

    cmd = ['snap', 'set', 'kube-apiserver'] + api_opts.to_s().split(' ')
    check_call(cmd)
    cmd = (['snap', 'set', 'kube-controller-manager'] +
           controller_opts.to_s().split(' '))
    check_call(cmd)
    cmd = ['snap', 'set', 'kube-scheduler'] + scheduler_opts.to_s().split(' ')
    check_call(cmd)
Exemple #16
0
def configure_apiserver(etcd_connection_string, leader_etcd_version):
    api_opts = {}

    # Get the tls paths from the layer data.
    layer_options = layer.options('tls-client')
    ca_cert_path = layer_options.get('ca_certificate_path')
    client_cert_path = layer_options.get('client_certificate_path')
    client_key_path = layer_options.get('client_key_path')
    server_cert_path = layer_options.get('server_certificate_path')
    server_key_path = layer_options.get('server_key_path')

    if is_privileged():
        api_opts['allow-privileged'] = 'true'
        set_state('kubernetes-master.privileged')
    else:
        api_opts['allow-privileged'] = 'false'
        remove_state('kubernetes-master.privileged')

    # Handle static options for now
    api_opts['service-cluster-ip-range'] = service_cidr()
    api_opts['min-request-timeout'] = '300'
    api_opts['v'] = '4'
    api_opts['tls-cert-file'] = server_cert_path
    api_opts['tls-private-key-file'] = server_key_path
    api_opts['kubelet-certificate-authority'] = ca_cert_path
    api_opts['kubelet-client-certificate'] = client_cert_path
    api_opts['kubelet-client-key'] = client_key_path
    api_opts['logtostderr'] = 'true'
    api_opts['insecure-bind-address'] = '127.0.0.1'
    api_opts['insecure-port'] = '8080'
    api_opts['storage-backend'] = leader_etcd_version
    api_opts['basic-auth-file'] = '/root/cdk/basic_auth.csv'
    api_opts['token-auth-file'] = '/root/cdk/known_tokens.csv'
    api_opts['service-account-key-file'] = '/root/cdk/serviceaccount.key'
    api_opts['kubelet-preferred-address-types'] = \
        '[InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP]'

    etcd_dir = '/root/cdk/etcd'
    etcd_ca = os.path.join(etcd_dir, 'client-ca.pem')
    etcd_key = os.path.join(etcd_dir, 'client-key.pem')
    etcd_cert = os.path.join(etcd_dir, 'client-cert.pem')

    api_opts['etcd-cafile'] = etcd_ca
    api_opts['etcd-keyfile'] = etcd_key
    api_opts['etcd-certfile'] = etcd_cert
    api_opts['etcd-servers'] = etcd_connection_string

    admission_control = [
        'Initializers',
        'NamespaceLifecycle',
        'LimitRanger',
        'ServiceAccount',
        'ResourceQuota',
        'DefaultTolerationSeconds'
    ]

    auth_mode = hookenv.config('authorization-mode')
    if 'Node' in auth_mode:
        admission_control.append('NodeRestriction')

    api_opts['authorization-mode'] = auth_mode

    if get_version('kube-apiserver') < (1, 6):
        hookenv.log('Removing DefaultTolerationSeconds from admission-control')
        admission_control.remove('DefaultTolerationSeconds')
    if get_version('kube-apiserver') < (1, 7):
        hookenv.log('Removing Initializers from admission-control')
        admission_control.remove('Initializers')
    api_opts['admission-control'] = ','.join(admission_control)

    configure_kubernetes_service('kube-apiserver', api_opts, 'api-extra-args')
    restart_apiserver()
def configure_apiserver():
    # TODO: investigate if it's possible to use config file to store args
    # https://github.com/juju-solutions/bundle-canonical-kubernetes/issues/315
    # Handle api-extra-args config option
    to_add, to_remove = get_config_args()

    api_opts = FlagManager('kube-apiserver')

    # Remove arguments that are no longer provided as config option
    # this allows them to be reverted to charm defaults
    for arg in to_remove:
        hookenv.log('Removing option: {}'.format(arg))
        api_opts.destroy(arg)
        # We need to "unset" options by settig their value to "null" string
        cmd = ['snap', 'set', 'kube-apiserver', '{}=null'.format(arg)]
        check_call(cmd)

    # Get the tls paths from the layer data.
    layer_options = layer.options('tls-client')
    ca_cert_path = layer_options.get('ca_certificate_path')
    client_cert_path = layer_options.get('client_certificate_path')
    client_key_path = layer_options.get('client_key_path')
    server_cert_path = layer_options.get('server_certificate_path')
    server_key_path = layer_options.get('server_key_path')

    if is_privileged():
        api_opts.add('allow-privileged', 'true', strict=True)
        set_state('kubernetes-master.privileged')
    else:
        api_opts.add('allow-privileged', 'false', strict=True)
        remove_state('kubernetes-master.privileged')

    # Handle static options for now
    api_opts.add('service-cluster-ip-range', service_cidr())
    api_opts.add('min-request-timeout', '300')
    api_opts.add('v', '4')
    api_opts.add('tls-cert-file', server_cert_path)
    api_opts.add('tls-private-key-file', server_key_path)
    api_opts.add('kubelet-certificate-authority', ca_cert_path)
    api_opts.add('kubelet-client-certificate', client_cert_path)
    api_opts.add('kubelet-client-key', client_key_path)
    api_opts.add('logtostderr', 'true')
    api_opts.add('insecure-bind-address', '127.0.0.1')
    api_opts.add('insecure-port', '8080')
    api_opts.add('storage-backend', 'etcd2')  # FIXME: add etcd3 support

    admission_control = [
        'Initializers', 'NamespaceLifecycle', 'LimitRanger', 'ServiceAccount',
        'ResourceQuota', 'DefaultTolerationSeconds'
    ]

    auth_mode = hookenv.config('authorization-mode')
    if 'Node' in auth_mode:
        admission_control.append('NodeRestriction')

    api_opts.add('authorization-mode', auth_mode, strict=True)

    if get_version('kube-apiserver') < (1, 6):
        hookenv.log('Removing DefaultTolerationSeconds from admission-control')
        admission_control.remove('DefaultTolerationSeconds')
    if get_version('kube-apiserver') < (1, 7):
        hookenv.log('Removing Initializers from admission-control')
        admission_control.remove('Initializers')
    api_opts.add('admission-control', ','.join(admission_control), strict=True)

    # Add operator-provided arguments, this allows operators
    # to override defaults
    for arg in to_add:
        hookenv.log('Adding option: {} {}'.format(arg[0], arg[1]))
        # Make sure old value is gone
        api_opts.destroy(arg[0])
        api_opts.add(arg[0], arg[1])

    cmd = ['snap', 'set', 'kube-apiserver'] + api_opts.to_s().split(' ')
    check_call(cmd)
    set_state('kube-apiserver.do-restart')
def configure_kubelet(dns, ingress_ip):
    layer_options = layer.options('tls-client')
    ca_cert_path = layer_options.get('ca_certificate_path')
    server_cert_path = layer_options.get('server_certificate_path')
    server_key_path = layer_options.get('server_key_path')

    kubelet_opts = {}
    kubelet_opts['require-kubeconfig'] = 'true'
    kubelet_opts['kubeconfig'] = kubeconfig_path
    kubelet_opts['network-plugin'] = 'cni'
    kubelet_opts['v'] = '0'
    kubelet_opts['logtostderr'] = 'true'
    kubelet_opts['node-ip'] = ingress_ip
    kubelet_opts['allow-privileged'] = set_privileged()

    kubelet_cloud_config_path = cloud_config_path('kubelet')
    if is_state('endpoint.aws.ready'):
        kubelet_opts['cloud-provider'] = 'aws'
    elif is_state('endpoint.gcp.ready'):
        kubelet_opts['cloud-provider'] = 'gce'
        kubelet_opts['cloud-config'] = str(kubelet_cloud_config_path)
    elif is_state('endpoint.openstack.ready'):
        kubelet_opts['cloud-provider'] = 'openstack'
        kubelet_opts['cloud-config'] = str(kubelet_cloud_config_path)
    elif is_state('endpoint.vsphere.joined'):
        # vsphere just needs to be joined on the worker (vs 'ready')
        kubelet_opts['cloud-provider'] = 'vsphere'
        # NB: vsphere maps node product-id to its uuid (no config file needed).
        uuid_file = '/sys/class/dmi/id/product_uuid'
        with open(uuid_file, 'r') as f:
            uuid = f.read().strip()
        kubelet_opts['provider-id'] = 'vsphere://{}'.format(uuid)
    elif is_state('endpoint.azure.ready'):
        azure = endpoint_from_flag('endpoint.azure.ready')
        kubelet_opts['cloud-provider'] = 'azure'
        kubelet_opts['cloud-config'] = str(kubelet_cloud_config_path)
        kubelet_opts['provider-id'] = azure.vm_id

    if get_version('kubelet') >= (1, 10):
        # Put together the KubeletConfiguration data
        kubelet_config = {
            'apiVersion': 'kubelet.config.k8s.io/v1beta1',
            'kind': 'KubeletConfiguration',
            'address': '0.0.0.0',
            'authentication': {
                'anonymous': {
                    'enabled': False
                },
                'x509': {
                    'clientCAFile': ca_cert_path
                }
            },
            'clusterDomain': dns['domain'],
            'failSwapOn': False,
            'port': 10250,
            'tlsCertFile': server_cert_path,
            'tlsPrivateKeyFile': server_key_path
        }
        if dns['enable-kube-dns']:
            kubelet_config['clusterDNS'] = [dns['sdn-ip']]
        if is_state('kubernetes-worker.gpu.enabled'):
            kubelet_config['featureGates'] = {'DevicePlugins': True}

        # Add kubelet-extra-config. This needs to happen last so that it
        # overrides any config provided by the charm.
        kubelet_extra_config = hookenv.config('kubelet-extra-config')
        kubelet_extra_config = yaml.load(kubelet_extra_config)
        merge_kubelet_extra_config(kubelet_config, kubelet_extra_config)

        # Render the file and configure Kubelet to use it
        os.makedirs('/root/cdk/kubelet', exist_ok=True)
        with open('/root/cdk/kubelet/config.yaml', 'w') as f:
            f.write('# Generated by kubernetes-worker charm, do not edit\n')
            yaml.dump(kubelet_config, f)
        kubelet_opts['config'] = '/root/cdk/kubelet/config.yaml'
    else:
        # NOTE: This is for 1.9. Once we've dropped 1.9 support, we can remove
        # this whole block and the parent if statement.
        kubelet_opts['address'] = '0.0.0.0'
        kubelet_opts['anonymous-auth'] = 'false'
        kubelet_opts['client-ca-file'] = ca_cert_path
        kubelet_opts['cluster-domain'] = dns['domain']
        kubelet_opts['fail-swap-on'] = 'false'
        kubelet_opts['port'] = '10250'
        kubelet_opts['tls-cert-file'] = server_cert_path
        kubelet_opts['tls-private-key-file'] = server_key_path
        if dns['enable-kube-dns']:
            kubelet_opts['cluster-dns'] = dns['sdn-ip']
        if is_state('kubernetes-worker.gpu.enabled'):
            kubelet_opts['feature-gates'] = 'DevicePlugins=true'

    if get_version('kubelet') >= (1, 11):
        kubelet_opts['dynamic-config-dir'] = '/root/cdk/kubelet/dynamic-config'

    configure_kubernetes_service(configure_prefix, 'kubelet', kubelet_opts,
                                 'kubelet-extra-args')