kubernetes API调用相当于';kubectl apply';

kubernetes API调用相当于';kubectl apply';,kubernetes,Kubernetes,我尝试使用主api来更新资源 在1.2中,为了更新部署资源,我正在执行kubectl apply-f new updatedeployment.yaml 如何使用api执行相同的操作 我在pkg/kubectl/cmd/apply.go中检查了代码,我认为以下代码行显示了运行kubectl apply-f时的幕后情况: // Compute a three way strategic merge patch to send to server. patch, err := strategicpa

我尝试使用主api来更新资源

在1.2中,为了更新部署资源,我正在执行
kubectl apply-f new updatedeployment.yaml


如何使用api执行相同的操作

我在
pkg/kubectl/cmd/apply.go
中检查了代码,我认为以下代码行显示了运行
kubectl apply-f
时的幕后情况:

// Compute a three way strategic merge patch to send to server.
patch, err := strategicpatch.CreateThreeWayMergePatch(original, modified, current, 
    versionedObject, true)
helper := resource.NewHelper(info.Client, info.Mapping)
_, err = helper.Patch(info.Namespace, info.Name, api.StrategicMergePatchType, patch)
下面是代码
帮助程序。修补程序

func (m *Helper) Patch(namespace, name string, pt api.PatchType, data []byte) (runtime.Object, error) {
    return m.RESTClient.Patch(pt).
        NamespaceIfScoped(namespace, m.NamespaceScoped).
        Resource(m.Resource).
        Name(name).
        Body(data).
        Do().
        Get()
}
所以这个API的设计并不令人信服,因为它迫使我们在客户端重新实现这些基本的东西

无论如何,这里是我在Python中重新发明六边形轮子的尝试

Python模块kube_应用 用法类似于
kube\u apply.fromYaml(myStuff)

  • 可以读取字符串或打开的文件流(通过lib Yaml)
  • 处理带有多个连接对象的yaml文件
  • 实施是相当死板的,而且是第一次尝试 插入资源。如果失败,它会尝试一个补丁, 如果这也失败了,它会删除资源并 重新插入它
玩得开心

文件:
kube\u apply.py

#!/usr/bin/python3
# coding: utf-8
# __________   ________________________________________________   #
# kube_apply - apply Yaml similar to kubectl apply -f file.yaml   #
#                                                                 #
# (C) 2019 Hermann Vosseler <Ichthyostega@web.de>                 #
# This is OpenSource software; licensed under Apache License v2+  #
# ############################################################### #
'''
Utility for the official Kubernetes python client: apply Yaml data.
While still limited to some degree, this utility attempts to provide
functionality similar to `kubectl apply -f`
- load and parse Yaml
- try to figure out the object type and API to use
- figure out if the resource already exists, in which case
  it needs to be patched or replaced alltogether.
- otherwise just create a new resource.

Based on inspiration from `kubernetes/utils/create_from_yaml.py`

@since: 2/2019
@author: Ichthyostega
'''


import re
import yaml
import logging

import kubernetes.client



def runUsageExample():
    ''' demonstrate usage by creating a simple Pod through default client
    '''
    logging.basicConfig(level=logging.DEBUG)
    #
#   KUBECONFIG = '/path/to/special/kubecfg.yaml'
#   import kubernetes.config
#   client = kubernetes.config.new_client_from_config(config_file=KUBECONFIG)
#   # --or alternatively--
#   kubernetes.config.load_kube_config(config_file=KUBECONFIG)


    fromYaml('''
kind: Pod
apiVersion: v1
metadata:
  name: dummy-pod
  labels:
    blow: job
spec:
  containers:
  - name: sleepr
    image: busybox
    command:
    - /bin/sh
    - -c
    - sleep 24000
''')



def fromYaml(rawData, client=None, **kwargs):
    ''' invoke the K8s API to create or replace an object given as YAML spec.
        @param rawData: either a string or an opened input stream with a
                        YAML formatted spec, as you'd use for `kubectl apply -f`
        @param client: (optional) preconfigured client environment to use for invocation
        @param kwargs: (optional) further arguments to pass to the create/replace call
        @return: response object from Kubernetes API call
    '''
    for obj in yaml.load_all(rawData):
        createOrUpdateOrReplace(obj, client, **kwargs)


def createOrUpdateOrReplace(obj, client=None, **kwargs):
    ''' invoke the K8s API to create or replace a kubernetes object.
        The first attempt is to create(insert) this object; when this is rejected because
        of an existing object with same name, we attempt to patch this existing object.
        As a last resort, if even the patch is rejected, we *delete* the existing object
        and recreate from scratch.
        @param obj: complete object specification, including API version and metadata.
        @param client: (optional) preconfigured client environment to use for invocation
        @param kwargs: (optional) further arguments to pass to the create/replace call
        @return: response object from Kubernetes API call
    '''
    k8sApi = findK8sApi(obj, client)
    try:
        res = invokeApi(k8sApi, 'create', obj, **kwargs)
        logging.debug('K8s: %s created -> uid=%s', describe(obj), res.metadata.uid)
    except kubernetes.client.rest.ApiException as apiEx:
        if apiEx.reason != 'Conflict': raise
        try:
            # asking for forgiveness...
            res = invokeApi(k8sApi, 'patch', obj, **kwargs)
            logging.debug('K8s: %s PATCHED -> uid=%s', describe(obj), res.metadata.uid)
        except kubernetes.client.rest.ApiException as apiEx:
            if apiEx.reason != 'Unprocessable Entity': raise
            try:
                # second attempt... delete the existing object and re-insert
                logging.debug('K8s: replacing %s FAILED. Attempting deletion and recreation...', describe(obj))
                res = invokeApi(k8sApi, 'delete', obj, **kwargs)
                logging.debug('K8s: %s DELETED...', describe(obj))
                res = invokeApi(k8sApi, 'create', obj, **kwargs)
                logging.debug('K8s: %s CREATED -> uid=%s', describe(obj), res.metadata.uid)
            except Exception as ex:
                message = 'K8s: FAILURE updating %s. Exception: %s' % (describe(obj), ex)
                logging.error(message)
                raise RuntimeError(message)
    return res


def patchObject(obj, client=None, **kwargs):
    k8sApi = findK8sApi(obj, client)
    try:
        res = invokeApi(k8sApi, 'patch', obj, **kwargs)
        logging.debug('K8s: %s PATCHED -> uid=%s', describe(obj), res.metadata.uid)
        return res
    except kubernetes.client.rest.ApiException as apiEx:
        if apiEx.reason == 'Unprocessable Entity':
            message = 'K8s: patch for %s rejected. Exception: %s' % (describe(obj), apiEx)
            logging.error(message)
            raise RuntimeError(message)
        else:
            raise


def deleteObject(obj, client=None, **kwargs):
    k8sApi = findK8sApi(obj, client)
    try:
        res = invokeApi(k8sApi, 'delete', obj, **kwargs)
        logging.debug('K8s: %s DELETED. uid was: %s', describe(obj), res.details and res.details.uid or '?')
        return True
    except kubernetes.client.rest.ApiException as apiEx:
        if apiEx.reason == 'Not Found':
            logging.warning('K8s: %s does not exist (anymore).', describe(obj))
            return False
        else:
            message = 'K8s: deleting %s FAILED. Exception: %s' % (describe(obj), apiEx)
            logging.error(message)
            raise RuntimeError(message)



def findK8sApi(obj, client=None):
    ''' Investigate the object spec and lookup the corresponding API object
        @param client: (optional) preconfigured client environment to use for invocation
        @return: a client instance wired to the apriopriate API
    '''
    grp, _, ver = obj['apiVersion'].partition('/')
    if ver == '':
        ver = grp
        grp = 'core'
    # Strip 'k8s.io', camel-case-join dot separated parts. rbac.authorization.k8s.io -> RbacAuthorzation
    grp = ''.join(part.capitalize() for part in grp.rsplit('.k8s.io', 1)[0].split('.'))
    ver = ver.capitalize()

    k8sApi = '%s%sApi' % (grp, ver)
    return getattr(kubernetes.client, k8sApi)(client)


def invokeApi(k8sApi, action, obj, **args):
    ''' find a suitalbe function and perform the actual API invocation.
        @param k8sApi: client object for the invocation, wired to correct API version
        @param action: either 'create' (to inject a new objet) or 'replace','patch','delete'
        @param obj: the full object spec to be passed into the API invocation
        @param args: (optional) extraneous arguments to pass
        @return: response object from Kubernetes API call
    '''
    # transform ActionType from Yaml into action_type for swagger API
    kind = camel2snake(obj['kind'])
    # determine namespace to place the object in, supply default
    try: namespace = obj['metadata']['namespace']
    except: namespace = 'default'

    functionName = '%s_%s' %(action,kind)
    if hasattr(k8sApi, functionName):
        # namespace agnostic API
        function = getattr(k8sApi, functionName)
    else:
        functionName = '%s_namespaced_%s' %(action,kind)
        function = getattr(k8sApi, functionName)
        args['namespace'] = namespace
    if not 'create' in functionName:
        args['name'] = obj['metadata']['name']
    if 'delete' in functionName:
        from kubernetes.client.models.v1_delete_options import V1DeleteOptions
        obj = V1DeleteOptions()

    return function(body=obj, **args)


def describe(obj):
    return "%s '%s'" % (obj['kind'], obj['metadata']['name'])

def camel2snake(string):
    string = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', string)
    string = re.sub('([a-z0-9])([A-Z])', r'\1_\2', string).lower()
    return string


if __name__=='__main__':
    runUsageExample()
#/usr/bin/python3
#编码:utf-8
# __________   ________________________________________________   #
#kube_apply-apply Yaml类似于kubectl apply-f file.Yaml#
#                                                                 #
#(C)2019年Hermann Vosseler#
#这是开源软件;根据Apache许可证v2+获得许可#
# ############################################################### #
'''
官方Kubernetes python客户端实用程序:应用Yaml数据。
虽然在某种程度上仍然受到限制,但该实用程序试图提供
类似于'kubectl apply-f'的功能`
-加载和解析Yaml
-尝试找出要使用的对象类型和API
-找出资源是否已经存在,在这种情况下
它需要一起修补或更换。
-否则,只需创建一个新资源。
灵感来源于“kubernetes/utils/create_from_yaml.py”`
@自:2019年2月
@作者:鱼鳞病
'''
进口稀土
进口yaml
导入日志记录
导入kubernetes.client
def runUsageExample():
''通过默认客户端创建一个简单的Pod来演示用法
'''
logging.basicConfig(级别=logging.DEBUG)
#
#KUBECONFIG='/path/to/special/kubecfg.yaml'
#导入kubernetes.config
#client=kubernetes.config.new\u client\u from\u config(config\u file=KUBECONFIG)
##——或者--
#load\u kube\u config(config\u file=KUBECONFIG)
fromYaml(“”)
种类:豆荚
版本:v1
元数据:
名称:虚拟吊舱
标签:
打击:工作
规格:
容器:
-姓名:睡眠者
图片:busybox
命令:
-/bin/sh
--c
-睡眠24000
''')
来自YAML的def(原始数据,客户端=无,**kwargs):
''调用K8s API创建或替换作为YAML规范给定的对象。
@param rawData:字符串或带有
YAML格式的规范,如“kubectl apply-f”所用`
@param client:(可选)用于调用的预配置客户端环境
@param kwargs:(可选)传递给create/replace调用的其他参数
@返回:来自Kubernetes API调用的响应对象
'''
对于yaml.load_all(原始数据)中的对象:
createOrUpdateOrReplace(对象、客户端,**kwargs)
def createOrUpdateOrReplace(对象,客户端=无,**kwargs):
''调用K8s API以创建或替换kubernetes对象。
第一次尝试是创建(插入)此对象;当这被拒绝时,因为
对于同名的现有对象,我们尝试修补此现有对象。
作为最后手段,如果连补丁都被拒绝,我们将*删除*现有对象
从头开始。
@param obj:完整的对象规范,包括API版本和元数据。
@param client:(可选)用于调用的预配置客户端环境
@param kwargs:(可选)传递给create/replace调用的其他参数
@返回:来自Kubernetes API调用的响应对象
'''
k8sApi=findK8sApi(obj,客户)
尝试:
res=invokeApi(k8sApi,'create',obj,**kwargs)
logging.debug('K8s:%s created->uid=%s',description(obj),res.metadata.uid)
除了kubernetes.client.rest.ApiException作为apiEx之外:
如果apiEx.reason!='冲突:提高
尝试:
#请求原谅。。。
res=invokeApi(k8sApi,'patch',obj,**kwargs)
logging.debug('K8s:%s PATCHED->uid=%s',description(obj),res.metadata.uid)
除了kubernetes.client.rest.ApiException作为apiEx之外:
如果apiEx.reason!='无法处理的实体:raise
尝试:
#第二次尝试。。。删除现有对象并重新插入
logging.debug('K8s:替换%s失败。正在尝试删除和重新创建…',描述(obj))
res=invokeApi(k8sApi,'delete',obj,**kwargs)
logging.debug('K8s:%s DELETED…',description(obj))
res=invokeApi(k8sApi,'create',obj,**kwargs)
logging.debug('K8s:%s CREATED->uid=%s',description(obj),res.metadata.uid)
例外情况除外,例如:
消息='K8s:更新%s失败。异常:%s'(描述(对象),例如)
logging.error(消息)
引发运行时错误(消息)
返回res
def patchObject(对象,客户端=无,**kwargs):
k8sApi=findK8sApi(obj,客户)
尝试:
res=invokeApi(k8sApi,'patch',obj,**kwargs)
logging.debug('K8s:%s PATCHED->uid=%s',description(obj),res.metadata.uid)
返回res
除了kubernetes.client.rest.ApiException作为apiEx之外:
如果apiEx.reason==“不可处理实体”:
消息='K8s:已拒绝%s的修补程序。异常:%s'(描述(obj),apiEx)
logging.error(消息)