diff --git a/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/README.MD b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/README.MD new file mode 100644 index 00000000..867562fa --- /dev/null +++ b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/README.MD @@ -0,0 +1,8 @@ +# Best Practices for using Control-M to run a pod to completion in a Kubernetes-based cluster using a Helm chart + +| Objective | Corresponding CLI command | +|-----------------------|------------------------------------------------------------| +| Deploy the Agent Locally | ```helm upgrade -i control-m-agent ./control-m-agent -n controlm -f values.yaml``` | +| Get details | ```kubectl describe job “job name” This returns the pod name.``` | +| Monitor the job’s pod | ```kubectl get pod “pod name”``` | +| Get the job output | ```kubectl logs “pod name”``` | \ No newline at end of file diff --git a/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/.helmignore b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/Chart.yaml b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/Chart.yaml new file mode 100644 index 00000000..74a629e1 --- /dev/null +++ b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: control-m-agent +description: A BMC Control-M Agent Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.0.1 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.0.1" diff --git a/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/templates/RBAC.yaml b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/templates/RBAC.yaml new file mode 100755 index 00000000..585755e3 --- /dev/null +++ b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/templates/RBAC.yaml @@ -0,0 +1,42 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + type: stateful-service-ctmag-srv + name: statefulset-agent +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: statefulset-agent + labels: + type: stateful-service-ctmag-srv +rules: +- apiGroups: [""] + resources: ["pods", "pods/log", "pods/status"] + verbs: ["get", "list", "create", "update", "patch", "watch", "delete"] +- apiGroups: ["batch"] + resources: ["jobs"] + verbs: ["get", "list", "create", "update", "patch", "watch", "delete"] +{{- if .Values.runOnOpenShift }} +- apiGroups: ["security.openshift.io"] + resourceNames: ["anyuid"] + resources: ["securitycontextconstraints"] + verbs: ["use"] +{{- end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: statefulset-agent + labels: + type: stateful-service-ctmag-srv +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: statefulset-agent +subjects: +- kind: ServiceAccount + name: statefulset-agent + diff --git a/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/templates/pvc.yaml b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/templates/pvc.yaml new file mode 100644 index 00000000..6e5a266f --- /dev/null +++ b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/templates/pvc.yaml @@ -0,0 +1,10 @@ +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: agent-pvc +spec: + accessModes: + - {{ .Values.pvc.accessMode }} + resources: + requests: + storage: {{ .Values.pvc.volumeSize }} diff --git a/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/templates/secret.yaml b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/templates/secret.yaml new file mode 100644 index 00000000..64729a71 --- /dev/null +++ b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/templates/secret.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: controlmusercreds +type: Opaque +data: + AAPI_USER: "{{ .Values.controlM.api.user | b64enc }}" + AAPI_PASS: "{{ .Values.controlM.api.pass | b64enc }}" diff --git a/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/templates/service.yaml b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/templates/service.yaml new file mode 100644 index 00000000..dda3453c --- /dev/null +++ b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/templates/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: ctmag +spec: + clusterIP: None # headless service + selector: + type: stateful-service-ctmag-srv + ports: + - protocol: TCP + port: 80 + targetPort: {{ .Values.controlM.agent.agport }} diff --git a/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/templates/stateful.yaml b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/templates/stateful.yaml new file mode 100644 index 00000000..b29da576 --- /dev/null +++ b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/templates/stateful.yaml @@ -0,0 +1,61 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: statefulset-agent + labels: + type: stateful-service-ctmag-srv +spec: + serviceName: ctmag + selector: + matchLabels: + type: stateful-service-ctmag-srv + replicas: {{ .Values.controlM.agent.replicas }} # number of running agents + template: + metadata: + labels: + type: stateful-service-ctmag-srv + spec: + containers: + - name: ctmagent-container + # container in ECR + image: {{ .Values.controlM.agent.image }} + env: + - name: PERSISTENT_VOL + value: "{{ .Values.controlM.volumePath }}" + - name: CTM_SERVER_NAME + # update value to your Control-M/Server name + value: {{ .Values.controlM.serverName }} + - name: CTM_AGPORT + value: "{{ .Values.controlM.agent.agport }}" + - name: AAPI_END_POINT + # update value to your AAPI end point + value: "{{ .Values.controlM.api.endpoint }}" + - name: AAPI_USER + valueFrom: + secretKeyRef: + name: controlmusercreds + key: AAPI_USER + - name: AAPI_PASS + valueFrom: + secretKeyRef: + name: controlmusercreds + key: AAPI_PASS + imagePullPolicy: {{ .Values.controlM.agent.pullPolicy }} + ports: + - containerPort: {{ .Values.controlM.agent.agport }} + volumeMounts: + - name: pv-data + mountPath: "{{ .Values.controlM.volumePath }}" + terminationGracePeriodSeconds: 10 + # This sample is pulling the container from ECR. DockerHub is using imagePullSecret + # imagePullSecrets: + # - name: regcred + # See example_RBC.yaml for sample RBC Configuration + serviceAccountName: statefulset-agent + securityContext: + # the agent account gid so it will be able to access the PV + fsGroup: 1000 + volumes: + - name: pv-data + persistentVolumeClaim: + claimName: agent-pvc diff --git a/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/values.yaml b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/values.yaml new file mode 100644 index 00000000..6a8e02af --- /dev/null +++ b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/control-m-agent/values.yaml @@ -0,0 +1,24 @@ +# Default values for control-m-agent. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +runOnOpenShift: true + +pvc: + volumeSize: 10Gi + accessMode: ReadWriteOnce + +controlM: + serverName: + api: + endpoint: + user: change + pass: this + agent: + image: 000000000000.dkr.ecr.us-west-2.amazonaws.com/appdev-dockers-repo:agent-example + pullPolicy: IfNotPresent + replicas: 1 + agport: "7006" + volumePath: "/home/controlm/persistent_folder" + + \ No newline at end of file diff --git a/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/docker/Dockerfile b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/docker/Dockerfile new file mode 100644 index 00000000..9a29ded0 --- /dev/null +++ b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/docker/Dockerfile @@ -0,0 +1,124 @@ +#FROM registry.access.redhat.com/ubi8/python-38@sha256:af6f93b81f9313de95966e8cd681edb9dbcb5fdbddc5a4cc365af8e4534096ef as builder +FROM registry.access.redhat.com/ubi8/ubi:8.4 as builder +ARG AAPI_END_POINT +ARG AAPI_USER +ARG AAPI_PASS +ARG AGENT_IMAGE_NAME +ARG SUB_USER +ARG SUB_PWD +#ENV HOME=/home + +RUN subscription-manager register --username $SUB_USER --password $SUB_PWD \ + && subscription-manager attach --auto + +RUN yum -y install wget \ + && yum -y install procps \ + && yum -y install yp-tools \ + && yum -y install telnet \ + && yum -y install unzip \ + && yum -y install sudo \ + && yum -y install net-tools \ + && yum -y install tcsh \ + && yum -y install openssl \ + && yum -y install openssl-devel \ + && yum -y install gcc \ + && yum -y install make \ + && yum -y install zlib-devel \ + && yum -y install libffi-devel \ +# && yum -y install compat-libstdc++-33.x86_64 C++ mandatory \ + && cd /usr/src \ +# install python + && wget https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz \ + && tar -zxf Python-3.8.5.tgz \ + && cd /usr/src/Python-3.8.5 \ +# && wget https://www.python.org/ftp/python/2.7.15/Python-2.7.15.tgz \ +# && tar -zxf Python-2.7.15.tgz \ +# && cd /usr/src/Python-2.7.15 \ + && ./configure --prefix=/usr/local --enable-shared LDFLAGS="-Wl,-rpath /usr/local/lib" --with-system-ffi --with-lto --enable-optimization \ + && make altinstall \ +# install pip + && cd \ + && wget https://bootstrap.pypa.io/get-pip.py \ + && /usr/src/Python-3.8.5/python get-pip.py \ +# && /usr/src/Python-2.7.15/python get-pip.py \ + && ln -sf /usr/local/bin/pip3.8 /bin/pip3.8 \ + && pip3.8 install kubernetes \ +# && ln -sf /usr/local/bin/pip3.8 /bin/pip2.7 \ +# && pip2.7 install kubernetes \ +# cleanup python install files + && cd /usr/src \ + && rm -rf Python-3.8.5.tgz \ + && rm -rf /usr/src/Python-3.8.5 \ +# && rm -rf Python-2.7.15.tgz \ +# && rm -rf /usr/src/Python-2.7.15 \ +# install nodejs + && curl --silent --location https://rpm.nodesource.com/setup_12.x | bash - \ + && yum -y install nodejs \ + && node -v \ + && npm -v \ +# install aapi CLI + && curl -k -O $AAPI_END_POINT/ctm-cli.tgz \ + && npm install -g ctm-cli.tgz \ + && ctm -v \ + && rm -rf ctm-cli.tgz \ +# create controlm useruser + && useradd -d /home/controlm -s /bin/tcsh -m controlm \ + && chmod -R 755 /home/controlm \ +# add controlm user and root to soduers list + && echo 'root ALL=(ALL) ALL' >> /etc/sudoers \ + && echo 'controlm ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers \ + && yum clean all \ + && rm -rf /var/cache/yum +USER controlm +WORKDIR /home/controlm + +# Create AAPI env +RUN ctm env add myenv $AAPI_END_POINT $AAPI_USER $AAPI_PASS \ +# install java 8 + && sudo yum -y install java-1.8.0-openjdk-headless \ + && java -version \ +# install agent, setup will be done during statup + && ctm provision image $AGENT_IMAGE_NAME && echo installation ended successfully \ +# clean + && sudo yum -y autoremove java-1.8.0-openjdk-headless \ + && sudo yum clean all \ + && sudo rm -rf /var/cache/yum + + +# entry point script +COPY container_agent_startup.sh . +# script to run and monitor k8s jobs +COPY runJob.py . +# agent configuration file +COPY agent_configuration.json . + +#copy license +COPY LICENSE /licenses/LICENSE + +EXPOSE 7000-8000 +EXPOSE 22 + +# create final image - reduce size +FROM builder AS builderbase + +RUN sudo rm -rf /home +RUN sudo rm -rf /tmp/* + +FROM scratch AS leanimage + +LABEL name="Control-M Agent" \ + vendor="BMC" \ + version="v0.0.1" \ + release="1" \ + summary="Control-M/Agent image." \ + description="This is a Control-M/Agent image that planned to run in K8s env." \ + maintainer="ybergman@bmc.com" + +COPY --from=builderbase / / +COPY --from=builder --chown=controlm:controlm /home /home + +USER controlm +WORKDIR /home/controlm + + +ENTRYPOINT ["tcsh" , "-c" , "pwd ; ./container_agent_startup.sh $PERSISTENT_VOL $CTM_SERVER_NAME $CTM_AGPORT"] diff --git a/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/docker/LICENSE b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/docker/LICENSE new file mode 100644 index 00000000..2c7e6423 --- /dev/null +++ b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/docker/LICENSE @@ -0,0 +1,232 @@ + + +

+ Rev. 03-16-2017 +

+

+CONTROL-M DEVELOPERS KIT LICENSE AGREEMENT +

+

+BY OPENING THE PACKAGE, INSTALLING, CLICKING AGREE +OR YES, DOWNLOADING, OR USING THE CONTROL-M AUTOMATION DEVELOPERS KIT AND/OR +THE MATERIALS WITHIN THE KIT, THE ENTITY OR INDIVIDUAL ENTERING INTO THIS +AGREEMENT AGREES TO BE BOUND BY THE FOLLOWING TERMS AND THE TERMS OF ANY +THIRD-PARTY FILES OR PROGRAMS EMBEDDED IN THE CONTROL-M AUTOMATION DEVELOPERS +KIT (SUCH THIRD-PARTY TERMS ARE FOUND IN THE APPLICABLE INSTALLATION +INSTRUCTIONS, RELEASE NOTES, TEXT FILE, OR DISTRIBUTION FILE). IF YOU DO NOT +AGREE WITH ANY OF THESE TERMS, DO NOT INSTALL OR USE THE CONTROL-M AUTOMATION +DEVELOPERS KIT. IF YOU REJECT THIS AGREEMENT, YOU WILL NOT ACQUIRE ANY LICENSE +TO USE THE CONTROL-M AUTOMATION DEVELOPERS KIT. +

+

+This Control-M Developers Kit License Agreement +(Agreement) is an agreement between the entity or individual +entering into this Agreement (Licensee) and BMC Software, Inc., +a Delaware corporation (BMC), located at 2103 CityWest Blvd., +Houston, Texas 77042. In addition to the restrictions imposed under this +Agreement, any other usage restrictions contained in the Control-M Automation +Developers Kits installation instructions and release notes shall apply to Licensees +use of the Control-M Automation Developers Kit and all materials contained +therein. +

+

+1. SCOPE OF AGREEMENT. This +Agreement governs Licensees use of the Control-M Automation Developers Kit and +the materials it contains (collectively, the BMC Kit). +

+

+2. LICENSE. Subject to this +Agreement, BMC grants Licensee a non-exclusive, non-transferable, +non-sublicensable, revocable (per Section 3 of this Agreement), and +royalty-free right under trade secrets rights and copyrights to use the BMC Kit +for the following purposes: (a) to exercise the definition of workflows in Control-M +code format; (b) to run workflows in work bench mode; and (c) to use the APIs +to define and activate work flows made in Control-M code format with the +version of the Control-M software for which Licensee is licensed (Control-M +Product). The use of the BMC Kit as described in Section 2(c) above is +contingent on Licensee having a valid license to the Control-M Product. +

+

+3. TERMINATION. This Agreement +terminates automatically if Licensee breaches any of its terms. Use of the BMC +Kit as described under Sections 2(a) and 2(b) above in a non-development +environment or use of the BMC Kit as described in Section 2(c) above without +having a valid license to the Control-M Product will be considered a breach of this +Agreement. Either party may terminate this Agreement without cause. Upon termination, +Licensee must cease use of the BMC Kit and the BMC Kit must be promptly +destroyed and removed from any computer system. +

+

+4. OWNERSHIP. BMC, or its affiliates +or licensors, retains all right, title, and interest in the BMC Kit and copies thereof, +and any intellectual property, informational, industrial property, and +proprietary rights therein. The BMC Kit is protected by applicable copyright +and trade secret rights and may be protected by patent and other intellectual +and industrial property laws. BMC neither grants to Licensee any license to any +BMC patents nor transfers any rights of ownership in the BMC Kit to Licensee. +

+

+5. CONFIDENTIAL AND PROPRIETARY +INFORMATION. The +BMC Kit contains valuable confidential information and trade secrets of BMC. Licensee +agrees to use all reasonable efforts to prevent the unauthorized use, copying, +publication, or dissemination of the BMC Kit. Licensee may not disclose the +BMC Kit or any part thereof to any third party; provided, however, Licensee may +disclose the BMC Kit, or parts thereof, to Licensees employees provided that +such employee (i) is one that needs to receive the disclosure of the BMC Kit in +order to install, use, support or maintain the BMC Kit, and (ii) has +obligations of confidentiality with regard to the BMC Kit, which are at least +as protective of BMCs confidential information and trade secrets as the +provisions of this Agreement. +

+

+6. INDEMNIFICATION. If a third party asserts +a Claim (as defined herein) against BMC, Licensee will at its own expense defend +or settle the Claim and indemnify and hold harmless BMC, its affiliates, and +licensors, and each of their respective employees, officers, directors, and +representatives from and against any loss, claim, liability, damage, action or +cause of action (including reasonable attorneys fees) arising out of or +relating to any third party claim concerning Licensees use of the BMC Kit in +violation of this Agreement (a Claim). Licensee will not bind BMC +to a monetary obligation in a settlement or compromise, or make an admission on +behalf of BMC, without obtaining BMCs prior consent. +

+

+7. DISCLAIMER OF WARRANTIES. THE BMC KIT IS PROVIDED +AS IS, WITH ALL FAULTS. BMC, ITS AFFILIATES, AND LICENSORS SPECIFICALLY +DISCLAIM ALL WARRANTIES, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND QUIET +ENJOYMENT. BMC DOES NOT WARRANT THAT THE OPERATION OF THE BMC KIT WILL BE +UNINTERRUPTED OR VIRUS OR ERROR FREE, THAT THERE ARE NO DEFECTS, OR THAT ANY DEFECT +IN SUCH CAN BE CORRECTED.

+

+8. LIMITS ON BMCS LIABILITY. NEITHER BMC, ITS +AFFILIATES OR LICENSORS, WILL BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL, +PUNITIVE OR CONSEQUENTIAL DAMAGES RELATING TO OR ARISING OUT OF THIS AGREEMENT, +THE USE OF THE BMC KIT, OR ANY THIRD PARTY CODE OR SOFTWARE PROVIDED WITH THE +BMC KIT (INCLUDING, WITHOUT LIMITATION, LOST PROFITS, LOST COMPUTER USAGE TIME, +AND DAMAGE TO, OR LOSS OF USE OF DATA), EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES, AND IRRESPECTIVE OF ANY NEGLIGENCE OF BMC OR WHETHER SUCH DAMAGES +RESULT FROM A CLAIM ARISING UNDER TORT OR CONTRACT LAW. BMCS LIABILITY FOR +DIRECT DAMAGES UNDER THIS AGREEMENT, INCLUDING FOR INFRINGEMENT CLAIMS, IS +LIMITED TO THE GREATER OF (I) THE AMOUNT PAID BY LICENSEE FOR THE BMC KIT, IF +ANY, AND (II) $100.

+

+9. VERIFICATION. If requested by BMC, Licensee +will deliver to BMC written certification relating to Licensees use of the BMC +Kit in compliance with the terms of this Agreement. BMC may audit Licensees use +of the BMC Kit to confirm such compliance. +

+

10. EXPORT CONTROLS. Licensee represents and +warrants that it: a) will comply with the United States Export Administration +Regulations and other U.S. or foreign export regulations; b) no individual +accessing or using the BMC Kit is a citizen of or from an embargoed country +(currently Iran, Syria, Sudan, Cuba and North Korea); c) is not prohibited from +receiving the BMC Kit under such regulations; d) will not acquire the BMC Kit for +a person who is restricted under such regulations; e) will not use the BMC Kit in +contradiction to such regulations; and f) will not use the BMC Kit for +prohibited uses, including but not limited to nuclear, chemical, missile or +biological weapons related end uses. For BMC Kit exported from Ireland, EC No. +428/2009 sets up a Community regime for control of exports of dual-use items +and technology, and it is declared that this BMC Kit is intended for civil +purposes only. Therefore, Licensee agrees to comply with both the U.S. +regulations and those E.U. regulations and will not export in violation of the +regulations and without all proper licenses. Any failure to comply with these +regulations will result in Licensee forfeiting all rights to the BMC Kit. +

+

+11. GOVERNING LAW AND DISPUTE RESOLUTION. A party will provide written notice to the other party of any controversy, dispute or claim arising out of or relating to this Agreement, or to the formation, interpretation, breach, termination, or validity thereof (Controversy). The parties shall engage in good faith negotiations to resolve the Controversy. Only if the Controversy is not resolved through good faith negotiations within 15 days of the sending of the written notice of Controversy, the Controversy may be submitted to litigation or binding arbitration, based on the place of incorporation of the parties, as follows: +(i) If both parties to this Agreement are entities incorporated under the law of any state in the United States, the Controversy shall be tried in either state or federal court located in Houston, Texas and the laws of the State of Texas shall govern. Both sides hereby submit to the exclusive jurisdiction of the courts in Houston, Texas and waive all defenses based on forum non conveniens. +(ii) If both parties to this Agreement are entities incorporated in countries in the Europe, Middle East, or Africa regions, the arbitration shall be held in Amsterdam, Netherlands under the then-applicable rules of the International Chamber of Commerce and the substantive laws of the Netherlands will govern. +(iii) If both parties to this Agreement are entities incorporated in countries in the Asia Pacific region, the arbitration shall be held in Singapore under the then-applicable rules of the Singapore International Arbitration Centre and the substantive laws of Singapore will govern. +(iv) In all other instances, the arbitration shall be held in New York City, New York, under the then-applicable international rules of the American Arbitration Association and the substantive laws of the State of Texas will govern. +For all arbitrations conducted hereunder: (a) the arbitration shall be conducted in English; (b) the relevant arbitral institution shall determine the number of arbitrators, but any Controversy in which the amount in dispute is greater than $10 million USD shall be decided by three arbitrators, with each party having the right to select one arbitrator; (c) the costs of such arbitration shall be borne equally, pending the arbitrators award; (d) the arbitration award rendered shall be final and binding on the parties, shall not be subject to appeal to any court and shall be enforceable in any court having jurisdiction over the Parties; (e) the arbitration proceedings, award and pleadings shall all be confidential, unless disclosure of particular information is required for purposes of enforcing/challenging the award or to meet local securities law requirements; and (f) the party prevailing in arbitration shall be entitled to recover its reasonable attorneys fees and the necessary costs incurred in connection with the arbitration. +

+

+The United Nations Convention on Contracts for +the International Sale of Goods shall not apply to this Agreement. Nothing in +this Agreement shall be deemed as preventing either party from seeking +immediate injunctive relief from any court having jurisdiction over the parties +and the subject matter of the dispute. +

+

+12. U.S. FEDERAL ACQUISITIONS. This Section applies +only to acquisitions of the commercial BMC Kit subject to this Agreement by or +on behalf of the United States Government, or by any prime contractor or +subcontractor (at any tier) under any contract, grant, cooperative agreement or +other activity with the United States Government. In the event the BMC Kit is delivered +to the United States Government, the United States Government hereby agrees +that the BMC Kit qualifies as commercial items within the meaning of the +Federal acquisition regulation(s) applicable to this procurement. The terms and +conditions of this Agreement shall pertain to the United States Governments +use and disclosure of the BMC Kit, and shall supersede any conflicting +contractual terms and conditions. The following additional statement applies +only to acquisitions governed by DFARS Subpart 227.4 (October 1988): +Restricted Rights Use, duplication and disclosure by the Government is +subject to restrictions as set forth in subparagraph (c)(1)(ii) of the Rights +in Technical Data and Computer Software clause at DFARS 252.227-7013 (Oct. +1988). +

+

+13. ASSIGNMENT AND TRANSFERS. Licensee may not assign +or transfer the BMC Kit separate from this Agreement, and may not assign or +transfer this Agreement except in the event of a merger with or into, or a +transfer of all or substantially all of Licensees assets to, a third party who +assumes all of Licensees liabilities and obligations under the Agreement, and +expressly agrees in writing to be bound by and comply with all of the terms of this +Agreement. Except as specifically authorized by applicable law, any attempt to +assign or transfer this Agreement in violation of this provision will be null +and void and be treated as a violation of BMCs intellectual property rights or +use outside the scope of this Agreement. +

+

+14. DATA PROTECTION. (a) Licensee acknowledges +that BMC neither requires nor needs Licensee to (i) send BMC any personal data +collected by Licensee (Licensee Collected Data) or (ii) give +BMC access to any Licensee Collected Data. Consequently, Licensee remains +responsible for either filtering, making anonymous, encrypting such Licensee Collected +Data or for having proper procedures in place to prevent Licensee Collected +Data from being sent to or accessed by BMC. (b) In the course of normal +business, BMC may collect and process personal information related to the Licensee +(mainly contact and related information) in order to perform its obligations +under this Agreement, such information being referred to hereinafter as Licensee +Contact Information. Where the Licensee Contact Information is to be +processed by BMC, BMC will comply with its Controller and Processor Binding +Corporate Rules Policy found at http://media.cms.bmc.com/documents/External+Privacy+Binding+Coporate+Rules+Policy+-+Aug+04.pdf" +(the BCR) +with respect to compliance with data protection laws and/or regulations. The +BCR policy is incorporated into a BMC corporate wide policy, requiring all BMC +entities, employees and third party providers to comply with and respect the +BCR policy, which is governing the collection, use, access, storage and +transfer of personal data among BMC entities and third-party sub-processors. +The details of the BCR approval of BMC Software are available at +http://ec.europa.eu/justice/data-protection/international-transfers/binding-corporate-rules/bcr_cooperation/index_en.htm. BMC shall in +particular: (i) allow Licensee to access, modify, correct or erase Licensee Contact +Information when necessary; (ii) take reasonable technical and organizational +security measures to maintain the confidentiality and integrity of Licensee Contact +Information and to prevent its unauthorized access, use, or disclosure; and +(iii) refrain from using Licensee Contact Information for any other purpose +than performing its obligations under this Agreement. +

+

15. MISCELLANEOUS TERMS. A waiver by a party of +any breach of any term of this Agreement will not be construed as a waiver of +any continuing or succeeding breach. The parties acknowledge they have +read this Agreement and agree that it is the complete and exclusive statement +of the parties and supercedes any prior or contemporaneous negotiations or +agreements, between the parties relating to the BMC Kit and any subject matter related +to this Agreement. This Agreement may be modified only in a mutually-signed, +whether in writing or electronic, agreement between Licensee and BMC. Should +any term of this Agreement be invalid or unenforceable, the remaining terms will +remain in effect. The prevailing party in any litigation is entitled to recover +its attorneys fees and costs from the other party. Any delay or failure of +any party to perform any obligation under this Agreement caused by governmental +restrictions, labor disputes, storms or natural disasters, emergency, or other +causes beyond the reasonable control of the party, will not be deemed a breach +of this Agreement; provided, however, this provision does not apply to the +payment of monies or any breach of Section 5. The parties have agreed that this +Agreement and the documents related thereto be drawn up in the English +language. Les parties exigent que la prsente convention ainsi que les +documents qui sy rattachent soient rdigs en anglais. +

+ + diff --git a/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/docker/agent_configuration.json b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/docker/agent_configuration.json new file mode 100644 index 00000000..c559968e --- /dev/null +++ b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/docker/agent_configuration.json @@ -0,0 +1,3 @@ +{ + "connectionInitiator" : "AgentToServer" +} diff --git a/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/docker/build_docker_example.sh b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/docker/build_docker_example.sh new file mode 100644 index 00000000..1d7ee1c4 --- /dev/null +++ b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/docker/build_docker_example.sh @@ -0,0 +1,9 @@ +# This is a build command example with parameters +# You need to change the parameters: +# 1. endpoint, +# 2. user, +# 3. password +# 4. the agent image you want to install (taken from "ctm provision images Linux" cli) +sudo docker build --build-arg AAPI_END_POINT=https://:8443/automation-api --build-arg AAPI_USER=user --build-arg AAPI_PASS=password --build-arg AGENT_IMAGE_NAME=Agent_Image.Linux . + +# Don't forget to upload the result to your docker repository (ECR, DockerHub, etc.) for k8s use. diff --git a/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/docker/container_agent_startup.sh b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/docker/container_agent_startup.sh new file mode 100755 index 00000000..5520c081 --- /dev/null +++ b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/docker/container_agent_startup.sh @@ -0,0 +1,82 @@ +#!/bin/tcsh + +if (-f '.cshrc') then + source .cshrc +endif + +echo parameters: $argv +set AG_NODE_ID=`cat /etc/hostname` + +set PERSISTENT_VOL=$1/$AG_NODE_ID +set CTM_SERVER_NAME=$2 +set CTM_AGPORT=$3 + +if ("aa${CTM_AGPORT}aa" == "aaaa") then + echo missing parameters during container startup, use: --evn PERSISTENT_VOL=predefine persistent volume --env CTM_SERVER_NAME=ctm_name --env CTM_AGPORT=7006 + exit 1; +else + echo parameters validation passed +endif + + +# create if needed, and map agent persistent data folders +echo mapping persistent volume +cd /home/controlm +if (! -d $PERSISTENT_VOL/pid) then + echo first time the agent is using the persistent volume, moving folders to persistent volume + # no agent files exist in PV, copy the current agent files to PV + mkdir $PERSISTENT_VOL + mv $CONTROLM/backup $CONTROLM/capdef $CONTROLM/dailylog $CONTROLM/measure $CONTROLM/onstmt $CONTROLM/pid $CONTROLM/procid $CONTROLM/sysout $CONTROLM/status $CONTROLM/temp -t $PERSISTENT_VOL +else + echo this is not the first time an agent is running using this persistent volume, mapping folder to existing persistent volume + rm -Rf $CONTROLM/backup $CONTROLM/capdef $CONTROLM/dailylog $CONTROLM/measure $CONTROLM/onstmt $CONTROLM/pid $CONTROLM/procid $CONTROLM/sysout $CONTROLM/status $CONTROLM/temp +endif +# create link to persistent volume +ln -s $PERSISTENT_VOL/backup $CONTROLM/backup +ln -s $PERSISTENT_VOL/capdef $CONTROLM/capdef +ln -s $PERSISTENT_VOL/dailylog $CONTROLM/dailylog +ln -s $PERSISTENT_VOL/measure $CONTROLM/measure +ln -s $PERSISTENT_VOL/onstmt $CONTROLM/onstmt +ln -s $PERSISTENT_VOL/pid $CONTROLM/pid +ln -s $PERSISTENT_VOL/procid $CONTROLM/procid +ln -s $PERSISTENT_VOL/sysout $CONTROLM/sysout +ln -s $PERSISTENT_VOL/status $CONTROLM/status +ln -s $PERSISTENT_VOL/temp $CONTROLM/temp + +# point CLI to request endpoint if one was requested +if ($?AAPI_END_POINT && $?AAPI_USER && $?AAPI_PASS) then + echo using new AAPI configuration, not the default build time configuration + ctm env add prod $AAPI_END_POINT $AAPI_USER $AAPI_PASS + ctm env set prod +else + echo using the default build time AAPI configuration +endif + +# workaround to support a server located in a cloud environemnt +echo applying workaround to hosts file +setenv SERVERIP `echo $AAPI_END_POINT | grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'` +echo "$SERVERIP $CTM_SERVER_NAME" | sudo tee -a /etc/hosts + +echo configuring and registering the agent +# set JAVA_HOME to agent’s java 1.8 so provision can work +# provision the agent will also start it +if (-d /home/controlm/JRE) then + setenv JAVA_HOME /home/controlm/JRE + setenv PATH ${PATH}:/home/controlm/JRE/bin +else if (-d /home/controlm/bmcjava/bmcjava-V2) then + setenv JAVA_HOME /home/controlm/bmcjava/bmcjava-V2 + setenv PATH ${PATH}:/home/controlm/bmcjava/bmcjava-V2/bin +else if (-d /home/controlm/ctm/JRE) then + setenv JAVA_HOME /home/controlm/ctm/JRE + setenv PATH ${PATH}:/home/controlm/ctm/JRE/bin +else if (! $?JAVA_HOME) then + echo ERROR Unable to determine JAVA_HOME location +endif + +echo using JAVA HOME: $JAVA_HOME + #statements +ctm provision setup $CTM_SERVER_NAME $AG_NODE_ID $CTM_AGPORT -f agent_configuration.json + + +echo Running in agent container +sleep infinity diff --git a/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/docker/runJob.py b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/docker/runJob.py new file mode 100644 index 00000000..6ce8ea91 --- /dev/null +++ b/3-infrastructure-as-code-examples/kubernetes-agent-using-helm/docker/runJob.py @@ -0,0 +1,465 @@ +import re + +from os import path +from kubernetes import config, client, utils +from kubernetes.utils import FailToCreateError +from kubernetes.client.rest import ApiException +from pprint import pprint + +import getopt, os, sys, time +import signal +import yaml + +kNameSpace = "controlm" +kJobname = None +util_client = None +batch_client = None +core_client = None +stime = 3 # Default sleep interval in seconds for status check in seconds + +def get_job_name_from_ymal(yaml_file, verbose=False): + jobname = None + with open(path.abspath(yaml_file)) as f: + yml_document_all = yaml.safe_load_all(f) + + failures = [] + for yml_document in yml_document_all: + try: + jobname = get_job_name_from_dict( yml_document, verbose) + except FailToCreateError as failure: + failures.extend(failure.api_exceptions) + if failures: + raise FailToCreateError(failures) + return jobname + +def get_job_name_from_dict(data, verbose=False): + # If it is a list type, will need to iterate its items + api_exceptions = [] + job_name = None + if "List" in data["kind"]: + # Could be "List" or "Pod/Service/...List" + # This is a list type. iterate within its items + kind = data["kind"].replace("List", "") + for yml_object in data["items"]: + # Mitigate cases when server returns a xxxList object + # See kubernetes-client/python#586 + if kind != "": + yml_object["apiVersion"] = data["apiVersion"] + yml_object["kind"] = kind + try: + job_name = get_name_from_yaml_single_item(yml_object, verbose) + except client.rest.ApiException as api_exception: + api_exceptions.append(api_exception) + else: + # This is a single object. Call the single item method + try: + job_name = get_name_from_yaml_single_item(data, verbose) + except client.rest.ApiException as api_exception: + api_exceptions.append(api_exception) + + # In case we have exceptions waiting for us, raise them + if api_exceptions: + raise FailToCreateError(api_exceptions) + + return job_name + +def get_name_from_yaml_single_item(yml_object, verbose=False): + kind = yml_object["kind"] + kind = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', kind) + kind = re.sub('([a-z0-9])([A-Z])', r'\1_\2', kind).lower() + # Expect the user to create namespaced objects more often + name = yml_object["metadata"]["name"] + if verbose: + msg = "entity {0} name: {1}".format(kind, name) + print(msg) + return name + +def create_job_object(kJob, kImage, kVname, kVvalue, kimagepullpolicy, kimagepullsecret, krestartpolicy, + kbackofflimit, khostpath, kvolname, kvolpath, kpvolclaim, kcommands, kargs): + # This creates a job object dynamically but supports only limited parameters + # If you need any characteristics nt supported here, use a yaml manifest + # + + # Configure environment variables + env_list = [] + for key in kVname: + value = kVvalue[kVname.index(key)] + v1_envvar = client.V1EnvVar(name=key, value=value) + env_list.append(v1_envvar) + + # Configure Volume Devices and Mounts + volnames_list = [] + if kvolname != 'none': + volname = client.V1VolumeMount( + name=kvolname, + mount_path=kvolpath) + volnames_list.append(volname) + + # Configure Volumes list + vol_list = [] + if kvolname != 'none': + if kpvolclaim != 'none': + vol = client.V1Volume(name=kvolname, + persistent_volume_claim=client.V1PersistentVolumeClaimVolumeSource( + claim_name=kpvolclaim)) + else: + vol = client.V1Volume(name=kvolname, + host_path=client.V1HostPathVolumeSource(path=khostpath, + type='Directory')) + vol_list.append(vol) + + # Configure Pod template container + container = client.V1Container( + name="ctmjob", + image=kImage, + image_pull_policy=kimagepullpolicy, + env=env_list, + command=kcommands if len (kcommands) > 0 else None, + args=kargs if len(kargs) > 0 else None, + volume_mounts=volnames_list) + + # Configure Image Pull Secret(s) + imagesecrets = [] + isecret = client.V1LocalObjectReference(name=kimagepullsecret) + imagesecrets.append(isecret) + + # Create and configure a spec section + template = client.V1PodTemplateSpec( + metadata=client.V1ObjectMeta(name=kJob), + spec=client.V1PodSpec(containers=[container], + image_pull_secrets=imagesecrets, + restart_policy=krestartpolicy, + volumes=vol_list)) + + # Create the specification of deployment + spec = client.V1JobSpec( + template=template, + backoff_limit=kbackofflimit) + + # Instantiate the job object + job = client.V1Job( + api_version="batch/v1", + kind="Job", + metadata=client.V1ObjectMeta(name=kJob), + spec=spec) + + return job + +def createJob(api_batch, job): + try: + api_response = api_batch.create_namespaced_job(body=job, namespace=kNameSpace) + print("Job created. status='%s'" % str(api_response.status)) + except ApiException as e: + print("Exception when calling BatchV1Api->create_namespaced_job: %s\n" % e) + pprint(job) + sys.exit(3) + +def deleteJob(api_batch, api_core, kJobname, podName): + api_batch.delete_namespaced_job(kJobname, kNameSpace) + print("Job deleted: " + kJobname) + +# podBody = client.V1DeleteOptions() + ret = api_core.delete_namespaced_pod(podName, kNameSpace) + print("Pod deleted: " + podName) + +def getLog(api_core, podName): + ret = api_core.read_namespaced_pod_log(podName, kNameSpace) + print("Log output from Pod: " + podName) + print(ret) + return + +def listPod(api_core, kJobname): + podName = None + podLabelSelector = 'job-name=' + kJobname + print("Listing pod for jobname:" + kJobname) + ret = api_core.list_namespaced_pod(kNameSpace, label_selector=podLabelSelector) + for i in ret.items: + podName = str(i.metadata.name) + print("%s" % i.metadata.name) + return podName + +def startJob(api_util, batch_client, kJobname, yaml): + try: + utils.create_from_yaml(api_util, yaml, verbose=True) + except FailToCreateError as e: + print("Exception when calling UtilAPI->create_from_yaml: %s\n" % e) + sys.exit(5) + + try: + api_response = batch_client.read_namespaced_job(kJobname, kNameSpace) + pprint(api_response) + except ApiException as e: + print("Exception when calling BatchV1Api->read_namespaced_job: %s\n" % e) + sys.exit(6) + + print("Job {0} created".format(api_response.metadata.name)) + return + +def status(api_batch, kJobname): + print("Starting to track job status for: %s\n" % kJobname) + jobStatus = "Success" + jobRunning = "Running" + podLabelSelector = 'job-name=' + kJobname + time.sleep(stime) # Give the Pod a chance to initialize + + while jobRunning == "Running": + try: + ret = api_batch.list_namespaced_job(kNameSpace, label_selector=podLabelSelector) + if len(ret.items) <= 0: + print("job was deleted, no info found") + return (0, 0, 0, 0) + + except: + print("Failed getting job status: " + kJobname) + print("job was deleted, no info found") + return (0, 0, 0, 0) + + for i in ret.items: + print("Job status: active %s failed %s succeeded %s\n" % (i.status.active, i.status.failed, i.status.succeeded)) + jobStatus = str(i.status.active) + podsFailed = str(i.status.failed) + podsSucceeded = str(i.status.succeeded) + if jobStatus.isdigit(): + if jobStatus >= "1": + jobRunning = "Running" + time.sleep(stime) + else: + jobRunning = "Not Running" + else: + jobRunning = "Not Running" + + podsFailed = str(i.status.failed) + podsSucceeded = str(i.status.succeeded) + podsActive = str(i.status.active) + jobStatus = "1" # at least one container failed - job failed + if podsActive == "None": + podsActive = "0" + if podsSucceeded == "None": + podsSucceeded = "0" + if podsFailed == "None": + podsFailed = "0" + jobStatus = "0" # no contianer failed - job succeeded + + return int(jobStatus), podsActive, podsSucceeded, podsFailed + +def termSignal(signalNumber, frame): + #global kJobname, kNameSpace, podName, api_core + print("Terminating due to SIGTERM: " + signalNumber) + podName = listPod(core_client, kJobname) + getLog(core_client, podName) + deleteJob(batch_client, kJobname, podName) + sys.exit(8) + +def usage(): + print("\n\tjobname is the only mandatory parameter. If yaml manifest specified, all other options ignored.\n") + print("\t-a, --args\t\targuaments (default None, container args will be used)") + print("\t-b, --backofflimit\tdefault is 0") + print("\t-c, --claim\t\tname of persistent volume claim") + print("\t-e, --envname\t\tEnvironment variable name") + print("\t-H, --hostpath\t\tPath on host machine (must be a directory)") + print("\t-i, --image\t\tcontainer image name") + print("\t-j, --jobname\t\tMandatory. Job name") + print("\t-n, --namespace\t\tDefault is namespace of Agent statefulset/pod") + print("\t-m, --volname\t\tVolume mount name") + print("\t-k, --command\t\tcommand to run (default None, continer entrypoint will be used)") + print("\t-p, --image\t\tpull_policy Always or Latest") + print("\t-r, --restartpolicy\tefault is Never") + print("\t-s, --imagesecret\ttname of image_pull_secret") + print("\t-t, --volpath\t\tVolume mount path in Pod") + print("\t-v, --envvalue\t\tvariable value") + print("\t-y, --yaml\t\tname of a yaml manifest for job creation. Overrides all others except jobname") + +def used_opts(kJobname, kNameSpace, kYaml, kVname, + kVvalue, kImagename, kimagepullpolicy, + kimagepullsecret, krestartpolicy, + kbackofflimit, khostpath, + kvolname, kvolpath, kpvolclaim, kcommands, kargs): + + print("Command line options specified:") + print("\tjobname: %s \n" + "\tnamespace: %s \n" + "\tEnvironment Variables: %s \n" + "\tEnvironment Values: %s \n" + "\tContainer image: %s \n" + "\tImage_pull_policyy: %s \n" + "\tImage_pull_secret: %s \n" + "\tRestart_policy: %s \n" + "\tbackofflimit: %d \n" + "\tVolume Name: %s \n" + "\tHost path: %s \n" + "\tContainer Path: %s \n" + "\tPersistentVolumeClaim: %s \n" + "\tCommands: %s \n" + "\tArgs: %s \n" + "\tYaml file: %s \n" + % (kJobname, kNameSpace, kVname, + kVvalue, kImagename, kimagepullpolicy, + kimagepullsecret, krestartpolicy, + kbackofflimit, kvolname, khostpath, + kvolpath, kpvolclaim, kcommands, kargs, kYaml)) + +def main(argv): + kJobname = '' + kNameSpace = None + kYaml = '' + kVname = [] + kVvalue = [] + kImagename = 'none' + kpvolclaim = 'none' + kimagepullpolicy = 'Always' + kimagepullsecret = 'regcred' + krestartpolicy = 'Never' + kbackofflimit = 0 + khostpath = 'none' + kvolname = 'none' + kvolpath = 'none' + kcommands = [] + kargs = [] + + # Arguments: + # a|args arguments to pass to -k|Commands (default is None) + # b|backofflimit default is 0 + # c|claim PersistentVolumeClaim + # e|envname environment variable name + # H|hostpath Path on host machine (must be a directory} + # i|image container image name + # j|jobname Mandatory. Job name + # m|volname Volume mount name + # m|commands Commands to run in container (entrypoint) + # p|image_pull_policy Always or Latest + # r|restartpolicy default is Never + # s|imagesecret name of image_pull_secret + # t|volpath Volume mount path in Pod + # v|envvalue variable value + # y|yaml name of a yaml manifest for job creation. Overrides all others except jobname + # + try: + opts, args = getopt.getopt(argv,"hj:c:i:e:v:y:p:s:r:b:H:m:t:a:k:", + ["jobname=","claim=", "image=","envname=","envvalue=","yaml=","imagepullpolicy=", + "imagepullsecret=", "restartpolicy=", "backofflimit=", "hostpath=", + "volname=", "volpath=","commands=","args="]) + except getopt.GetoptError: + usage() + sys.exit(1) + for opt, arg in opts: + if opt == '-h': + usage() + sys.exit(0) + elif opt in ("-e", "--envname"): + kVname.append(arg) + elif opt in ("-c", "--claim"): + kpvolclaim = arg + elif opt in ("-i", "--image"): + kImagename = arg + elif opt in ("-j", "--jobname"): + kJobname = arg + elif opt in ("-n", "--namespace"): + kNameSpace = arg + elif opt in ("-v", "--envvalue"): + kVvalue.append(arg) + elif opt in ("-y", "--yaml"): + kYaml = arg + elif opt in ("-b", "--backofflimit"): + kbackofflimit = int(arg) + elif opt in ("-p", "--imagepullpolicy"): + kimagepullpolicy = arg + elif opt in ("-r", "--restartpolicy"): + krestartpolicy = arg + elif opt in ("-s", "--imagepullsecret"): + kimagepullsecret = arg + elif opt in ("-H", "--hostpath"): + khostpath = arg + elif opt in ("-m", "--volname"): + kvolname = arg + elif opt in ("-k", "--commands"): + kcommands.append(arg) + elif opt in ("-a", "--args"): + kargs.append(arg) + + if kNameSpace == None: + with open("/run/secrets/kubernetes.io/serviceaccount/namespace", 'r') as ns: + kNameSpace = ns.readlines() + + used_opts(kJobname, kNameSpace, kYaml, kVname, + kVvalue, kImagename, kimagepullpolicy, + kimagepullsecret, krestartpolicy, + kbackofflimit, khostpath, + kvolname, kvolpath,kpvolclaim, kcommands, kargs) + + if kJobname == '' and kYaml == '': + usage() + sys.exit(2) + elif kJobname == '' and kYaml != '': + kJobname = get_job_name_from_ymal(kYaml) + elif kJobname != '' and kYaml != '': + ymlJobName = get_job_name_from_ymal(kYaml) + if (ymlJobName != kJobname): + print("jobname -j '%s' not equal to the jobname in the yaml file '%s'" % (kJobname, ymlJobName)) + sys.exit(26) + + #config.load_kube_config() + clientConf = client.Configuration() + # assuming the script is running in pod: + # token in: /var/run/secrets/kubernetes.io/serviceaccount/token + # ca in: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + # api server: https://kubernetes.default + # (true for all pods) + with open('/var/run/secrets/kubernetes.io/serviceaccount/token') as f: + token = f.read() + clientConf.host = 'https://kubernetes.default' + clientConf.api_key['authorization'] = token + clientConf.api_key_prefix['authorization'] = 'Bearer' + clientConf.ssl_ca_cert = '/var/run/secrets/kubernetes.io/serviceaccount/ca.crt' + # can also use the default in-cluster way + #ApiClient client = ClientBuilder.cluster().build(); + + util_client = client.ApiClient(clientConf) + batch_client = client.BatchV1Api(util_client) + core_client = client.CoreV1Api(util_client) + job_exist = False + + try: + podName = listPod(core_client, kJobname) + # There can't be more than 1 deployment (kube job) with the same name + # do we want to delete ? + job_exist = podName is not None + except ApiException as e: + print("Exception when calling BatchV1Api->read_namespaced_job_status: %s\n" % e) + sys.exit(16) + + + if not job_exist: + print("job not exist - creating (starting) job") + if kYaml != '': + print("Yaml specified. All other arguments - besides jobname - ignored") + startJob(util_client, batch_client, kJobname, kYaml) + else: + job = create_job_object(kJobname, kImagename, kVname, kVvalue, kimagepullpolicy, kimagepullsecret, + krestartpolicy, kbackofflimit, khostpath, kvolname, kvolpath, kpvolclaim, kcommands, kargs) + try: + createJob(batch_client, job) + except: + print("Job creation failed") + sys.exit(16) + else: + print("job already exist - start monitoring") + + #signal.signal(signal.SIGTERM, termSignal) + jobStatus, podsActive, podsSucceeded, podsFailed = status(batch_client, kJobname) + + + if (podName is None): + # incase the job started (initial list returned none since the job didnt exist) + podName = listPod(core_client, kJobname) + + getLog(core_client, podName) + + print("Pods Statuses: %s Running / %s Succeeded / %s Failed" % (podsActive, podsSucceeded, podsFailed)) + print("Job Completion status: %d " % (jobStatus)) + + deleteJob(batch_client, core_client, kJobname, podName) + + sys.exit(jobStatus) + +if __name__ == '__main__': + main(sys.argv[1:])