├── .gitignore ├── LICENSE ├── README.md ├── cde └── sdk │ └── go │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── cmd │ ├── artifact.go │ ├── branch.go │ ├── build.go │ ├── env.go │ ├── pipelinerun.go │ ├── repository.go │ ├── root.go │ ├── service.go │ └── taskrun.go │ ├── go.mod │ ├── go.sum │ ├── hack │ ├── generate.sh │ ├── gofmt.sh │ ├── install_golint.sh │ └── linter.sh │ ├── main.go │ └── pkg │ └── cdf │ └── events │ ├── event_types.go │ └── extensions │ ├── artifact_extension.go │ └── service_extension.go ├── docs ├── Events Workstream Meeting Notes 2020.md ├── SIG Meeting Notes 2021.md ├── SIG Meeting Notes 2022.md ├── SIG Meeting Notes 2023.md ├── SIG Meeting Notes current.md ├── Vocabulary Workstream Meeting Notes 2021.md ├── Vocabulary Workstream Meeting Notes 2022.md └── Vocabulary Workstream Meeting Notes current.md ├── poc ├── CDF-events-PoC.drawio ├── CDF-events-PoC.png ├── CDF-events-Spinnaker-PoC.drawio ├── CDF-events-Spinnaker-PoC.png ├── README.md ├── cloudplayer │ └── deploy.yaml ├── demo-script.md ├── docker │ ├── CDF-events-PoC.png │ ├── Dockerfile │ └── index.html ├── poc.sh ├── resources │ ├── README.md │ ├── ce.json │ └── shipyard.yaml ├── sequence-spin-cdevents.png ├── sequence-spin-cdevents.txt ├── sequence.txt ├── sig-events-functional.drawio ├── sig-events-functional.png ├── sig-events-sequence.png ├── sig-events-spinnaker-functional.drawio ├── sig-events-spinnaker-functional.png ├── spinnaker │ ├── deploy-spinnaker-poc.json │ ├── deploySpinnakerWithHalyard.sh │ ├── installAndConfigSpinnaker.sh │ ├── spin-echo-deploy.yaml │ └── spinnakerServiceAccount.yaml └── tekton │ ├── README.md │ ├── rbac.yaml │ ├── resources │ ├── 0000_namespace.yaml │ ├── 0001_serviceaccount.yaml │ ├── 0002_eventlistener.yaml │ ├── 0003_triggers.yaml │ └── 0004_pipelines.yaml │ └── workspace-template.yaml └── vocabulary-draft ├── README.md ├── continuous-deployment-pipeline-events.md ├── continuous-integration-pipeline-events.md ├── core.md ├── introduction.md └── source-code-version-control.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | cde/sdk/go/build/* 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CDF Events Special Interest Group - **Archived** 2 | 3 | The initial work produced by the Events SIG is available in this repository and it 4 | lead to the creation of the [CDEvents Project](https://cdevents.dev). 5 | 6 | The conversation about interoperability and events continues at the CDF through the work 7 | of the [CDEvents Project](https://cdevents.dev) and its 8 | [CDEvents Implementation Working Group](https://github.com/cdevents/implementation-wg). 9 | 10 | If you are interested in events and interoperability please join us at the CDEvents project. 11 | 12 | ## Quick links 13 | 14 | - [Meeting Information](#meetings) 15 | - [Members](#members) 16 | - [New Members](#new-members) 17 | - [Governance](#governance) 18 | - [Communication](#communication) 19 | - [Meetings](#meetings) 20 | 21 | ## Group Charter 22 | 23 | ### Introduction 24 | 25 | Today’s CI/CD systems do not talk to each other in a standardized way. This leads to problems related to interoperability, notification of failure issues, and poor automation. 26 | 27 | This group is looking at how events can help to create CI/CD systems with a decoupled architecture that is easy to scale and makes it resilient to failures. Using events could also increase automation when connecting workflows from different systems to each other, and as a result empowering tracing/visualizing/auditing of the connected workflows through these events. 28 | 29 | ### Purpose 30 | 31 | The group will focus on the use of events to provide interoperability through topics like: 32 | 33 | - When are events suited? For triggers, audits, monitoring, management 34 | - Common guidelines for at-least-once, at-most-once, exactly once, ordering… When to apply what strategy? 35 | - Best practices for event-driven CI/CD systems 36 | - Events to be used by tools for orchestration/workflows 37 | - Pipeline to pipeline communication via events 38 | - Tracing/auditing/graphing/visualizing of the entire process, e.g., through events. What truly occurs? 39 | - Metrics, e.g., how many versions have been deployed, how many PRs (Pull Requests) have been raised, how many events have been issued? 40 | - How are events related and how are they ordered (links vs trace context)? 41 | 42 | ### Outcome 43 | 44 | The group is working on a standardized event protocol that caters for technology agnostic machine-to-machine communication in CI/CD systems. This specification will be published, reviewed and agreed upon between relevant Linux Foundation projects/members. This work can be followed through the [CDEvents website](https://cdevents.dev/). 45 | 46 | The group aims to provide reference implementations such as event listeners and event senders on top of CloudEvents. These implementations are / will be published under the [CDEvents GitHub organization](https://github.com/cdevents). 47 | 48 | The group also produces blog posts, whitepapers and presentations published within the CD Foundation. 49 | 50 | ### History 51 | 52 | This is group started as a work-stream within the CDF SIG Interoperability. 53 | The forming of the workstream was suggested on a [SIG Interoperability meeting](https://github.com/cdfoundation/sig-interoperability/blob/master/docs/meetings_2020.md#May-28-2020) and its first meeting was held on June 8th 2020. 54 | The SIG was formed in February 2021. 55 | 56 | ## What is an event? 57 | 58 | We currently stick with the [definition used by CloudEvents](https://github.com/cloudevents/spec/blob/v1.0/spec.md#terminology): 59 | An *event* is a data record expressing an *occurrence* and its context, where *occurrence* is the capture of a statement of fact during the operation of a software system. 60 | 61 | ## Members 62 | 63 | Current members: 64 | 65 | - Ravi Lachhman, [@ravilach](https://github.com/ravilach), Harness 66 | - Andreas Grimmer, [@agrimmer](https://github.com/agrimmer), Dynatrace 67 | - Emil Bäckmark, [@e-backmark-ericsson](https://github.com/e-backmark-ericsson), Ericsson 68 | - Ramin Akhbari, ([@rakhbari](https://github.com/rakhbari)), eBay 69 | - Mattias Linnér, [@m-linner-ericsson](https://github.com/m-linner-ericsson), Ericsson 70 | - Andrea Frittoli, [@afrittoli](https://github.com/afrittoli), IBM 71 | - Mauricio Salatino [@salaboy](https://github.com/salaboy), [VMware](https://vmware.com) [Knative Project](http://knative.dev) 72 | - Steve Taylor [@sbtaylor15](https://github.com/sbtaylor15), [DeployHub](https://www.deployhub.com) / [Ortelius OS](https://ortelius.io) 73 | - Tracy Ragan [@tracyragan](https://github.com/tracyragan), [DeployHub](https://www.deployhub.com) / [Ortelius OS](https://ortelius.io) 74 | - Brad McCoy [@bradmccooydev](https://github.com/bradmccoydev), [Ortelius OS](https://ortelius.io) 75 | - Erik Sternerson [@erkist](https://github.com/erkist), doWhile 76 | - Cameron Motevasselani ([@link108](https://github.com/link108)), [Armory](https://www.armory.io/) 77 | - Alois Reitbauer ([@aloisreitbauer](https://github.com/aloisreitbauer)), [Dynatrace](https://www.dynatrace.com/) 78 | - Fredrik Fristedt [@fredjn](https://github.com/fredjn), Axis Communications 79 | - Oleg Nenashev [@oleg-nenashev](https://github.com/oleg-nenashev), [Dynatrace](https://www.dynatrace.com/)/[Jenkins](https://jenkins.io)/[Keptn](https://keptn.sh) 80 | - Kara de la Marck [@MarckK](https://github.com/MarckK), Continuous Delivery Foundation 81 | - Kevin Chu [@kbychu](https://github.com/kbychu), [GitLab](https://gitlab.com/) 82 | - Vibhav Bobade [@waveywaves](https://github.com/waveywaves), Independent 83 | - Matheus Paes [@MatheusPaes](https://github.com/MatheusPaes), [PicPay](https://picpay.com/) 84 | - Ishan Khare [@ishankhare07](https://github.com/ishankhare07), [Loft Labs](https://loft.sh/) 85 | - Brett Smith ([@xbcsmith](https://github.com/xbcsmith)), SAS Institute Inc. 86 | - Moteesh Reddy [@moteesh-in2tive](https://github.com/moteesh-in2tive), [Ozone](https://ozone.one/) 87 | - Marcus Philip [@marcusphi](https://github.com/marcusphi), [Avanza](https://www.avanza.se/) 88 | - Adam Gardner [@agardnerit](https://github.com/agardnerit), [OpenFeature](https://openfeature.dev)/[Keptn](https://keptn.sh) 89 | - Muktesh Mishra [@codedevil](https://github.com/mukteshkrmishra), Independent 90 | 91 | ### New Members 92 | 93 | Membership to this SIG is open to public and self-declared. 94 | 95 | New members are advised to: 96 | 97 | - Join the [SIG](https://lists.cd.foundation/g/sig-events) and 98 | [CDF TOC](https://lists.cd.foundation/g/cdf-toc) mailing lists. 99 | - Join the [CDF Slack](https://join.slack.com/t/cdeliveryfdn/shared_invite/zt-nwc0jjd0-G65oEpv5ynFfPD5oOX5Ogg) and jump into the [#sig-events channel](https://cdeliveryfdn.slack.com/archives/C0151BTKEJX) and introduce yourself. 100 | - Go through the [README.md](README.md) document. 101 | - Regularly join the SIG meetings. 102 | - Submit a PR to add yourself to the [members list](#members). 103 | - Here are various ways to get involved: 104 | - Share your thoughts by joining the meetings, posting to the mailing list, discussions forum or Slack channel. 105 | - Add a topic you would like to discuss to [the agenda](https://hackmd.io/xjK5ujQbTHSaEZjoY28b8g) of upcoming meeting. 106 | - Create a [new issue](https://github.com/cdfoundation/sig-events/issues) or start a [discussion](https://github.com/cdfoundation/sig-events/discussions) to start gathering feedback and collaborating. 107 | - Choose an issue where [help is needed](https://github.com/cdfoundation/sig-events/issues/labels/help%20wanted) and comment on it expressing interest. 108 | 109 | ## Communication 110 | 111 | - Mailing list: [lists.cd.foundation/g/sig-events](https://lists.cd.foundation/g/sig-events). 112 | - GitHub Discussions: [cdfoundation/sig-events/discussions](https://github.com/cdfoundation/sig-events/discussions). 113 | - Slack: #sig-events [slack channel](https://cdeliveryfdn.slack.com/archives/C0151BTKEJX) on the CDF slack. 114 | 115 | ## Governance 116 | 117 | SIG Events is a [CDF Special Interest Group](https://github.com/cdfoundation/toc/tree/master/sigs). 118 | 119 | The process SIG Events follows can be seen from [here](https://github.com/cdfoundation/toc/blob/master/GROUPS.md#sigs). 120 | 121 | Chairs and the TOC Sponsor of the SIG are 122 | 123 | * Emil Bäckmark ([@e-backmark-ericsson](https://github.com/e-backmark-ericsson)), Ericsson - Co-Chair 124 | * Andrea Frittoli ([@afrittoli](https://github.com/afrittoli)), IBM - Co-chair 125 | * Isaac Mosquera ([@imosquera](https://github.com/imosquera)), Armory - TOC Sponsor 126 | 127 | ## Meetings 128 | 129 | SIG Events meets monthly on Tuesdays at 3pm UTC in the summer time and at 4pm UTC in winter time. (*See your timezone [here](https://time.is/3pm_in_UTC)*). 130 | 131 | - [Meeting agenda and minutes](./docs/SIG%20Meeting%20Notes%20current.md). 132 | - [Zoom Bridge](https://zoom.us/j/98408983891?pwd=VXBxMjJTaThGRGFWRXFmdUxsRUZUdz09). 133 | - [Zoom International dial-in numbers](https://zoom.us/zoomconference). 134 | - [CDF Public Calendar (UTC)](https://calendar.google.com/calendar/u/0/embed?src=linuxfoundation.org_mhf0kmgedn67ihni8r129avp24@group.calendar.google.com&ctz=UTC). 135 | - [Recorded meetings on Youtube](https://www.youtube.com/playlist?list=PL2KXbZ9-EY9RlxWAnAjxs8Azuz11XVhkC) 136 | - [Archived meeting notes (2022)](./docs/SIG%20Meeting%20Notes%202022.md). 137 | - [Archived meeting notes (2021)](./docs/SIG%20Meeting%20Notes%202021.md). 138 | - [Archived meeting notes (2020)](./docs/Events%20Workstream%20Meeting%20Notes%202020.md). 139 | 140 | ## Artifacts 141 | 142 | The [CDEvents](https://cdevents.dev) is born out of the SIG Events group. 143 | CDEvents specification, SDKs and any project specific artifact is hosted under the [CDEvents GitHub org](https://github.com/cdevents). 144 | 145 | The following folders in the SIG Events repo are archived (read only): 146 | - `cde/sdk/go`: please refer to the [Go SDK](https://github.com/cdevents/sdk-go) 147 | - `vocabulary-drafr`: please refer to the [CDEvents Spec](https://github.com/cdevents/spec) 148 | 149 | The `poc` folder is temporarily hosted in this repo, and will be moved to 150 | the CDEvents GitHub org. 151 | -------------------------------------------------------------------------------- /cde/sdk/go/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /cde/sdk/go/Makefile: -------------------------------------------------------------------------------- 1 | # Make does not offer a recursive wildcard function, so here's one: 2 | rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2)) 3 | 4 | SHELL := /bin/bash 5 | NAME := cde 6 | BUILD_TARGET = build 7 | MAIN_SRC_FILE=main.go 8 | GO := GO111MODULE=on go 9 | GO_NOMOD :=GO111MODULE=off go 10 | REV := $(shell git rev-parse --short HEAD 2> /dev/null || echo 'unknown') 11 | ORG := cdfoundation 12 | ORG_REPO := $(ORG)/$(NAME) 13 | RELEASE_ORG_REPO := $(ORG_REPO) 14 | ROOT_PACKAGE := github.com/$(ORG_REPO)/$(NAME)/sdk/go 15 | GO_VERSION := 1.16 16 | GO_DEPENDENCIES := $(call rwildcard,pkg/,*.go) $(call rwildcard,cmd/,*.go) 17 | 18 | BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2> /dev/null || echo 'unknown') 19 | BUILD_DATE := $(shell date +%Y%m%d-%H:%M:%S) 20 | CGO_ENABLED = 0 21 | 22 | REPORTS_DIR=$(BUILD_TARGET)/reports 23 | 24 | GOTEST := $(GO) test 25 | 26 | # set dev version unless VERSION is explicitly set via environment 27 | VERSION ?= $(shell echo "$$(git for-each-ref refs/tags/ --count=1 --sort=-version:refname --format='%(refname:short)' 2>/dev/null)-dev+$(REV)" | sed 's/^v//') 28 | 29 | ifdef DISABLE_TEST_CACHING 30 | GOTEST += -count=1 31 | endif 32 | 33 | TEST_PACKAGE ?= ./... 34 | COVER_OUT:=$(REPORTS_DIR)/cover.out 35 | COVERFLAGS=-coverprofile=$(COVER_OUT) --covermode=count --coverpkg=./... 36 | 37 | build: $(GO_DEPENDENCIES) clean 38 | go mod download 39 | CGO_ENABLED=$(CGO_ENABLED) $(GO) $(BUILD_TARGET) -o build/$(NAME) $(MAIN_SRC_FILE) 40 | 41 | test: ## Run tests with the "unit" build tag 42 | CGO_ENABLED=$(CGO_ENABLED) $(GOTEST) --tags=unit -failfast -short ./... 43 | 44 | 45 | install: $(GO_DEPENDENCIES) ## Install the binary 46 | GOBIN=${GOPATH}/bin $(GO) install $(MAIN_SRC_FILE) 47 | 48 | linux: ## Build for Linux 49 | CGO_ENABLED=$(CGO_ENABLED) GOOS=linux GOARCH=amd64 $(GO) $(BUILD_TARGET) -o build/linux/$(NAME) $(MAIN_SRC_FILE) 50 | chmod +x build/linux/$(NAME) 51 | 52 | arm: ## Build for ARM 53 | CGO_ENABLED=$(CGO_ENABLED) GOOS=linux GOARCH=arm $(GO) $(BUILD_TARGET) -o build/arm/$(NAME) $(MAIN_SRC_FILE) 54 | chmod +x build/arm/$(NAME) 55 | 56 | win: ## Build for Windows 57 | CGO_ENABLED=$(CGO_ENABLED) GOOS=windows GOARCH=amd64 $(GO) $(BUILD_TARGET) -o build/win/$(NAME)-windows-amd64.exe $(MAIN_SRC_FILE) 58 | 59 | darwin: ## Build for OSX 60 | CGO_ENABLED=$(CGO_ENABLED) GOOS=darwin GOARCH=amd64 $(GO) $(BUILD_TARGET) -o build/darwin/$(NAME) $(MAIN_SRC_FILE) 61 | chmod +x build/darwin/$(NAME) 62 | 63 | .PHONY: goreleaser 64 | goreleaser: 65 | step-go-releaser --organisation=$(ORG) --revision=$(REV) --branch=$(BRANCH) --build-date=$(BUILD_DATE) --go-version=$(GO_VERSION) --root-package=$(ROOT_PACKAGE) --version=$(VERSION) 66 | 67 | .PHONY: release 68 | release: clean linux test 69 | 70 | release-all: release linux win darwin 71 | 72 | .PHONY: clean 73 | clean: ## Clean the generated artifacts 74 | rm -rf build release dist 75 | 76 | .PHONY: fmt 77 | fmt: importfmt ## Format the code 78 | $(eval FORMATTED = $(shell $(GO) fmt ./...)) 79 | @if [ "$(FORMATTED)" == "" ]; \ 80 | then \ 81 | echo "All Go files properly formatted"; \ 82 | else \ 83 | echo "Fixed formatting for: $(FORMATTED)"; \ 84 | fi 85 | 86 | .PHONY: importfmt 87 | importfmt: get-fmt-deps 88 | @echo "Formatting the imports..." 89 | goimports -w $(GO_DEPENDENCIES) 90 | 91 | get-fmt-deps: ## Install test dependencies 92 | $(GO_NOMOD) get golang.org/x/tools/cmd/goimports 93 | 94 | .PHONY: lint 95 | lint: ## Lint the code 96 | ./hack/gofmt.sh 97 | ./hack/linter.sh 98 | ./hack/generate.sh 99 | 100 | .PHONY: all 101 | all: fmt build test lint 102 | -------------------------------------------------------------------------------- /cde/sdk/go/README.md: -------------------------------------------------------------------------------- 1 | # CDE - CloudEvents for Continuous Delivery 2 | 3 | Simple CLI and library to emit Cloud Events related to Continuous Delivery. 4 | 5 | The framework and command-line interface enables you to emit and receive events related to Continuous Delivery, with the purpose of enabling features such as interoperability between different tools/services, performance measurements, increased visibility into your Continuous Delivery processes, etc. 6 | 7 | 8 | ## Usage 9 | 10 | Download the binary `cde` or build from source, by cloning this repository and run `make build` 11 | 12 | Set `CDE_SINK` environment variable to define where CloudEvents will be sent. You can do this by running: 13 | 14 | `export CDE_SINK=http://my-cloudevent-broker` 15 | 16 | You can use [SockEye](https://github.com/n3wscott/sockeye) to visualize the cloud events. 17 | 18 | Integrate with your existing pipelines, repositories, and environments. 19 | 20 | ## Supported Events 21 | 22 | Use `cde --help` 23 | 24 | The following events are supported: 25 | - [Environment Events](#environment-events) 26 | - [Service Events](#service-events) 27 | - [PipelineRun Events](#pipelinerun-events) 28 | - [TaskRun Events](#taskrun-events) 29 | - [Repository Events](#repository-events) 30 | 31 | 32 | # CDE Events 33 | 34 | The following events are currently supported: 35 | 36 | ## Environment Events 37 | - Properties 38 | - Id 39 | - Name 40 | - URL 41 | 42 | Examples: 43 | 44 | - **cd.environment.created.v1** `./cde env created --id staging --name "Staging Environment" --repo http://github.com/user/myrepo --data event=data` 45 | - **cd.environment.modified.v1** `./cde env modified --id staging --name "Staging Environment" --repo http://github.com/user/myrepo --data event=data` 46 | - **cd.environment.deleted.v1** `./cde env deleted --id staging --name "Staging Environment" --repo http://github.com/user/myrepo --data event=data` 47 | 48 | 49 | ## Service Events 50 | - Properties 51 | - Name 52 | - Version 53 | - Environment Id 54 | 55 | Examples: 56 | 57 | - **cd.service.deployed.v1** `./cde service deployed --envId staging --name my-service --version 1.0.2 --data service=data` 58 | - **cd.service.upgraded.v1** `./cde service upgraded --envId staging --name my-service --version 1.0.3 --data service=data` 59 | - **cd.service.rolledback.v1** `./cde service rolledback --envId staging --name my-service --version 1.0.2 --data service=data` 60 | - **cd.service.removed.v1** `./cde service removed --envId staging --name my-service --version 1.0.2 --data service=data` 61 | 62 | ## PipelineRun Events 63 | - Properties 64 | - Id 65 | - Name 66 | - URL 67 | - Status 68 | - Errors 69 | 70 | Examples: 71 | - **cd.pipelinerun.queued.v1** `./cde pipelinerun queued --id pipe1 --name "My Pipeline" --status "Queued" --url "http://my-pipelinerunner" --errors "Hopfully no errors" --data pipeline=data` 72 | - **cd.pipelinerun.started.v1** `./cde pipelinerun started --id pipe1 --name "My Pipeline" --status "Starting" --url "http://my-pipelinerunner" --errors "Hopfully no errors" --data pipeline=data` 73 | - **cd.pipelinerun.finished.v1** `./cde pipelinerun finished --id pipe1 --name "My Pipeline" --status "Finished" --url "http://my-pipelinerunner" --errors "Hopfully no errors" --data pipeline=data` 74 | 75 | ## TaskRun Events 76 | - Properties 77 | - Id 78 | - Name 79 | - PipelineId 80 | - Status 81 | - Errors 82 | 83 | Examples: 84 | - **cd.taskrun.started.v1** `./cde taskrun started --id task1 --name "My Task Run" --pipelineid pipe1 --data task=data` 85 | - **cd.taskrun.finished.v1** `./cde taskrun finished --id task1 --name "My Task Run" --pipelineid pipe1 --data task=data` 86 | 87 | ## Repository Events 88 | - Properties 89 | - Id 90 | - Name 91 | - URL 92 | 93 | Examples: 94 | - **cd.repository.created.v1** `./cde repository created --id repository_id --name "Name of repository" --url "http://my-repository" --data repository=data` 95 | - **cd.repository.modified.v1** `./cde repository modified --id repository_id --name "Name of repository" --url "http://my-repository" --data repository=data` 96 | - **cd.repository.deleted.v1** `./cde repository deleted --id repository_id --name "Name of repository" --url "http://my-repository" --data repository=data` 97 | 98 | 99 | # References 100 | - [CDFoundation SIG Events Vocabulary Draft](https://github.com/cdfoundation/sig-events/tree/main/vocabulary-draft) 101 | -------------------------------------------------------------------------------- /cde/sdk/go/cmd/artifact.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CD Events SDK Authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "context" 20 | "log" 21 | 22 | cde "github.com/cdfoundation/sig-events/cde/sdk/go/pkg/cdf/events" 23 | cloudevents "github.com/cloudevents/sdk-go/v2" 24 | "github.com/spf13/cobra" 25 | ) 26 | 27 | func init() { 28 | rootCmd.AddCommand(artifactCmd) 29 | artifactCmd.AddCommand(artifactPackagedCmd) 30 | artifactCmd.AddCommand(artifactPublishedCmd) 31 | 32 | artifactCmd.PersistentFlags().StringVarP(&artifactParams.ArtifactId, "id", "i", "", "Artifact Id") 33 | artifactCmd.PersistentFlags().StringVarP(&artifactParams.ArtifactName, "name", "n", "", "Artifact Name") 34 | artifactCmd.PersistentFlags().StringVarP(&artifactParams.ArtifactVersion, "version", "v", "", "Artifact Version") 35 | artifactCmd.PersistentFlags().StringToStringVarP(&artifactParams.ArtifactData, "data", "d", map[string]string{}, "Artifact Data") 36 | } 37 | 38 | var artifactCmd = &cobra.Command{ 39 | Use: "artifact", 40 | Short: "Emit Artifact related Events", 41 | Long: `Emit Artifact related CloudEvent`, 42 | } 43 | 44 | var artifactParams = cde.ArtifactEventParams{} 45 | 46 | var artifactPackagedCmd = &cobra.Command{ 47 | Use: "packaged", 48 | Short: "Emit Artifact Packaged Event", 49 | Long: `Emit Artifact Packaged CloudEvent`, 50 | RunE: func(cmd *cobra.Command, args []string) error { 51 | c, err := cloudevents.NewDefaultClient() 52 | if err != nil { 53 | log.Fatalf("failed to create client, %v", err) 54 | return err 55 | } 56 | 57 | // Create an Event. 58 | event, _ := cde.CreateArtifactEvent(cde.ArtifactPackagedEventV1, artifactParams) 59 | 60 | event.SetSource(source) 61 | 62 | // Set a target. 63 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 64 | 65 | // Send that Event. 66 | log.Printf("sending event %s\n", event) 67 | 68 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 69 | log.Fatalf("failed to send, %v", result) 70 | return result 71 | } 72 | 73 | return nil 74 | }, 75 | } 76 | 77 | var artifactPublishedCmd = &cobra.Command{ 78 | Use: "finished", 79 | Short: "Emit Artifact Published Event", 80 | Long: `Emit Artifact Published CloudEvent`, 81 | RunE: func(cmd *cobra.Command, args []string) error { 82 | c, err := cloudevents.NewDefaultClient() 83 | if err != nil { 84 | log.Fatalf("failed to create client, %v", err) 85 | return err 86 | } 87 | 88 | // Create an Event. 89 | event, _ := cde.CreateArtifactEvent(cde.ArtifactPublishedEventV1, artifactParams) 90 | 91 | event.SetSource(source) 92 | 93 | // Set a target. 94 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 95 | 96 | // Send that Event. 97 | log.Printf("sending event %s\n", event) 98 | 99 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 100 | log.Fatalf("failed to send, %v", result) 101 | return result 102 | } 103 | 104 | return nil 105 | }, 106 | } 107 | -------------------------------------------------------------------------------- /cde/sdk/go/cmd/branch.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CD Events SDK Authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "context" 20 | "log" 21 | 22 | cde "github.com/cdfoundation/sig-events/cde/sdk/go/pkg/cdf/events" 23 | cloudevents "github.com/cloudevents/sdk-go/v2" 24 | "github.com/spf13/cobra" 25 | ) 26 | 27 | func init() { 28 | rootCmd.AddCommand(branchCmd) 29 | branchCmd.AddCommand(branchCreatedCmd) 30 | branchCmd.AddCommand(branchDeletedCmd) 31 | 32 | branchCmd.PersistentFlags().StringVarP(&branchId, "id", "i", "", "Branch Id") 33 | branchCmd.PersistentFlags().StringVarP(&branchName, "name", "n", "", "Branch Name") 34 | branchCmd.PersistentFlags().StringVarP(&branchRepositoryId, "repoid", "r", "", "Branch Repository Id") 35 | branchCmd.PersistentFlags().StringToStringVarP(&branchData, "data", "d", map[string]string{}, "Branch Data") 36 | } 37 | 38 | var branchCmd = &cobra.Command{ 39 | Use: "branch", 40 | Short: "Emit Branch related Events", 41 | Long: `Emit Branch related CloudEvent`, 42 | } 43 | 44 | var ( 45 | branchId string 46 | branchName string 47 | branchRepositoryId string 48 | branchData map[string]string 49 | ) 50 | 51 | var branchCreatedCmd = &cobra.Command{ 52 | Use: "created", 53 | Short: "Emit Branch Created Event", 54 | Long: `Emit Branch Created CloudEvent`, 55 | RunE: func(cmd *cobra.Command, args []string) error { 56 | c, err := cloudevents.NewDefaultClient() 57 | if err != nil { 58 | log.Fatalf("failed to create client, %v", err) 59 | return err 60 | } 61 | 62 | // Create an Event. 63 | event, _ := cde.CreateBranchEvent(cde.BranchCreatedEventV1, branchId, 64 | branchName, branchRepositoryId, branchData) 65 | 66 | event.SetSource(source) 67 | 68 | // Set a target. 69 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 70 | 71 | // Send that Event. 72 | log.Printf("sending event %s\n", event) 73 | 74 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 75 | log.Fatalf("failed to send, %v", result) 76 | return result 77 | } 78 | 79 | return nil 80 | }, 81 | } 82 | 83 | var branchDeletedCmd = &cobra.Command{ 84 | Use: "deleted", 85 | Short: "Emit Branch Deleted Event", 86 | Long: `Emit Branch Deleted CloudEvent`, 87 | RunE: func(cmd *cobra.Command, args []string) error { 88 | c, err := cloudevents.NewDefaultClient() 89 | if err != nil { 90 | log.Fatalf("failed to create client, %v", err) 91 | return err 92 | } 93 | 94 | // Create an Event. 95 | event, _ := cde.CreateBranchEvent(cde.BranchDeletedEventV1, branchId, 96 | branchName, branchRepositoryId, branchData) 97 | 98 | event.SetSource(source) 99 | 100 | // Set a target. 101 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 102 | 103 | // Send that Event. 104 | log.Printf("sending event %s\n", event) 105 | 106 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 107 | log.Fatalf("failed to send, %v", result) 108 | return result 109 | } 110 | 111 | return nil 112 | }, 113 | } 114 | -------------------------------------------------------------------------------- /cde/sdk/go/cmd/build.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CD Events SDK Authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "context" 20 | "log" 21 | 22 | cde "github.com/cdfoundation/sig-events/cde/sdk/go/pkg/cdf/events" 23 | cloudevents "github.com/cloudevents/sdk-go/v2" 24 | "github.com/spf13/cobra" 25 | ) 26 | 27 | func init() { 28 | rootCmd.AddCommand(buildCmd) 29 | buildCmd.AddCommand(buildStartedCmd) 30 | buildCmd.AddCommand(buildFinishedCmd) 31 | buildCmd.AddCommand(buildQueuedCmd) 32 | 33 | buildCmd.PersistentFlags().StringVarP(&buildId, "id", "i", "", "Build Id") 34 | buildCmd.PersistentFlags().StringVarP(&buildName, "name", "n", "", "Build Name") 35 | buildCmd.PersistentFlags().StringVarP(&buildArtifactId, "artifact", "a", "", "Build's Artifact Id") 36 | buildCmd.PersistentFlags().StringToStringVarP(&buildData, "data", "d", map[string]string{}, "Build Data") 37 | } 38 | 39 | var buildCmd = &cobra.Command{ 40 | Use: "build", 41 | Short: "Emit Build related Events", 42 | Long: `Emit Build related CloudEvent`, 43 | } 44 | 45 | var ( 46 | buildId string 47 | buildName string 48 | buildArtifactId string 49 | buildData map[string]string 50 | ) 51 | 52 | var buildStartedCmd = &cobra.Command{ 53 | Use: "started", 54 | Short: "Emit Build Started Event", 55 | Long: `Emit Build Started CloudEvent`, 56 | RunE: func(cmd *cobra.Command, args []string) error { 57 | c, err := cloudevents.NewDefaultClient() 58 | if err != nil { 59 | log.Fatalf("failed to create client, %v", err) 60 | return err 61 | } 62 | 63 | // Create an Event. 64 | event, _ := cde.CreateBuildEvent(cde.BuildStartedEventV1, buildId, 65 | buildName, buildArtifactId, buildData) 66 | 67 | event.SetSource(source) 68 | 69 | // Set a target. 70 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 71 | 72 | // Send that Event. 73 | log.Printf("sending event %s\n", event) 74 | 75 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 76 | log.Fatalf("failed to send, %v", result) 77 | return result 78 | } 79 | 80 | return nil 81 | }, 82 | } 83 | 84 | var buildFinishedCmd = &cobra.Command{ 85 | Use: "finished", 86 | Short: "Emit Build Finished Event", 87 | Long: `Emit Build Finished CloudEvent`, 88 | RunE: func(cmd *cobra.Command, args []string) error { 89 | c, err := cloudevents.NewDefaultClient() 90 | if err != nil { 91 | log.Fatalf("failed to create client, %v", err) 92 | return err 93 | } 94 | 95 | // Create an Event. 96 | event, _ := cde.CreateBuildEvent(cde.BuildFinishedEventV1, buildId, 97 | buildName, buildArtifactId, buildData) 98 | 99 | event.SetSource(source) 100 | 101 | // Set a target. 102 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 103 | 104 | // Send that Event. 105 | log.Printf("sending event %s\n", event) 106 | 107 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 108 | log.Fatalf("failed to send, %v", result) 109 | return result 110 | } 111 | 112 | return nil 113 | }, 114 | } 115 | 116 | var buildQueuedCmd = &cobra.Command{ 117 | Use: "queued", 118 | Short: "Emit PipelineRun Queued Event", 119 | Long: `Emit PipelineRun Queued CloudEvent`, 120 | RunE: func(cmd *cobra.Command, args []string) error { 121 | c, err := cloudevents.NewDefaultClient() 122 | if err != nil { 123 | log.Fatalf("failed to create client, %v", err) 124 | return err 125 | } 126 | 127 | // Create an Event. 128 | event, _ := cde.CreateBuildEvent(cde.BuildQueuedEventV1, buildId, 129 | buildName, buildArtifactId, buildData) 130 | 131 | event.SetSource(source) 132 | 133 | // Set a target. 134 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 135 | 136 | // Send that Event. 137 | log.Printf("sending event %s\n", event) 138 | 139 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 140 | log.Fatalf("failed to send, %v", result) 141 | return result 142 | } 143 | 144 | return nil 145 | }, 146 | } 147 | -------------------------------------------------------------------------------- /cde/sdk/go/cmd/env.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CD Events SDK Authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "log" 22 | 23 | cde "github.com/cdfoundation/sig-events/cde/sdk/go/pkg/cdf/events" 24 | cloudevents "github.com/cloudevents/sdk-go/v2" 25 | "github.com/spf13/cobra" 26 | ) 27 | 28 | func init() { 29 | rootCmd.AddCommand(envCmd) 30 | envCmd.AddCommand(envCreatedCmd) 31 | envCmd.AddCommand(envDeletedCmd) 32 | envCmd.AddCommand(envModifiedCmd) 33 | 34 | envCmd.PersistentFlags().StringVarP(&envId, "id", "i", "", "Environment's Id") 35 | envCmd.PersistentFlags().StringVarP(&envName, "name", "n", "", "Environment's Name") 36 | envCmd.PersistentFlags().StringVarP(&envRepoUrl, "repo", "r", "", "Environment's RepoUrl") 37 | envCmd.PersistentFlags().StringToStringVarP(&envData, "data", "d", map[string]string{}, "Environment's Data") 38 | } 39 | 40 | var envCmd = &cobra.Command{ 41 | Use: "env", 42 | Short: "Emit Environment related Events", 43 | Long: `Emit Environment related CloudEvent`, 44 | } 45 | 46 | var ( 47 | envId string 48 | envName string 49 | envRepoUrl string 50 | envData map[string]string 51 | ) 52 | 53 | var envCreatedCmd = &cobra.Command{ 54 | Use: "created", 55 | Short: "Emit Env Created Event", 56 | Long: `Emit Environment Created CloudEvent`, 57 | RunE: func(cmd *cobra.Command, args []string) error { 58 | c, err := cloudevents.NewDefaultClient() 59 | if err != nil { 60 | log.Fatalf("failed to create client, %v", err) 61 | return err 62 | } 63 | 64 | // Create an Event. 65 | event, _ := cde.CreateEnvironmentEvent(cde.EnvironmentCreatedEventV1, envId, envName, envRepoUrl, envData) 66 | 67 | event.SetSource(source) 68 | 69 | // Set a target. 70 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 71 | 72 | // Send that Event. 73 | log.Printf("sending event %s\n", event) 74 | 75 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 76 | log.Fatalf("failed to send, %v", result) 77 | return result 78 | } 79 | 80 | return nil 81 | }, 82 | } 83 | 84 | var envDeletedCmd = &cobra.Command{ 85 | Use: "deleted", 86 | Short: "Emit Environment Deleted Event", 87 | Long: `Emit Environment Deleted CloudEvent`, 88 | RunE: func(cmd *cobra.Command, args []string) error { 89 | c, err := cloudevents.NewDefaultClient() 90 | if err != nil { 91 | log.Fatalf("failed to create client, %v", err) 92 | return err 93 | } 94 | 95 | // Create an Event. 96 | event, _ := cde.CreateEnvironmentEvent(cde.EnvironmentDeletedEventV1, envId, envName, envRepoUrl, envData) 97 | 98 | event.SetSource(source) 99 | 100 | // Set a target. 101 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 102 | 103 | // Send that Event. 104 | log.Printf("sending event %s\n", event) 105 | 106 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 107 | log.Fatalf("failed to send, %v", result) 108 | return result 109 | } 110 | 111 | return nil 112 | }, 113 | } 114 | 115 | var envModifiedCmd = &cobra.Command{ 116 | Use: "modified", 117 | Short: "Emit Environment Modified Event", 118 | Long: `Emit Environment Modified CloudEvent`, 119 | RunE: func(cmd *cobra.Command, args []string) error { 120 | c, err := cloudevents.NewDefaultClient() 121 | if err != nil { 122 | log.Fatalf("failed to create client, %v", err) 123 | return err 124 | } 125 | 126 | // Create an Event. 127 | event, _ := cde.CreateEnvironmentEvent(cde.EnvironmentModifiedEventV1, envId, envName, envRepoUrl, envData) 128 | 129 | event.SetSource(source) 130 | 131 | // Set a target. 132 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 133 | 134 | // Send that Event. 135 | log.Printf("sending event %s\n", event) 136 | 137 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 138 | log.Fatalf("failed to send, %v", result) 139 | return result 140 | } 141 | 142 | return nil 143 | }, 144 | } 145 | -------------------------------------------------------------------------------- /cde/sdk/go/cmd/pipelinerun.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CD Events SDK Authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "log" 22 | 23 | cde "github.com/cdfoundation/sig-events/cde/sdk/go/pkg/cdf/events" 24 | cloudevents "github.com/cloudevents/sdk-go/v2" 25 | "github.com/spf13/cobra" 26 | ) 27 | 28 | func init() { 29 | rootCmd.AddCommand(pipelineRunCmd) 30 | pipelineRunCmd.AddCommand(pipelineRunStartedCmd) 31 | pipelineRunCmd.AddCommand(pipelineRunFinishedCmd) 32 | pipelineRunCmd.AddCommand(pipelineRunQueuedCmd) 33 | 34 | pipelineRunCmd.PersistentFlags().StringVarP(&pipelineRunId, "id", "i", "", "Pipeline Run Id") 35 | pipelineRunCmd.PersistentFlags().StringVarP(&pipelineRunName, "name", "n", "", "Pipeline Run's Name") 36 | pipelineRunCmd.PersistentFlags().StringVarP(&pipelineRunStatus, "status", "s", "", "Pipeline Run's Status") 37 | pipelineRunCmd.PersistentFlags().StringVarP(&pipelineRunURL, "url", "u", "", "Pipeline Run's URL") 38 | pipelineRunCmd.PersistentFlags().StringVarP(&pipelineRunErrors, "errors", "e", "", "Pipeline Run's Errors") 39 | pipelineRunCmd.PersistentFlags().StringToStringVarP(&pipelineRunData, "data", "d", map[string]string{}, "Pipeline Run's Data") 40 | } 41 | 42 | var pipelineRunCmd = &cobra.Command{ 43 | Use: "pipelinerun", 44 | Short: "Emit PipelineRun related Events", 45 | Long: `Emit PipelineRun related CloudEvent`, 46 | } 47 | 48 | var ( 49 | pipelineRunId string 50 | pipelineRunName string 51 | pipelineRunStatus string 52 | pipelineRunURL string 53 | pipelineRunErrors string 54 | pipelineRunData map[string]string 55 | ) 56 | 57 | var pipelineRunStartedCmd = &cobra.Command{ 58 | Use: "started", 59 | Short: "Emit PipelineRun Started Event", 60 | Long: `Emit PipelineRun Started CloudEvent`, 61 | RunE: func(cmd *cobra.Command, args []string) error { 62 | c, err := cloudevents.NewDefaultClient() 63 | if err != nil { 64 | log.Fatalf("failed to create client, %v", err) 65 | return err 66 | } 67 | 68 | // Create an Event. 69 | event, _ := cde.CreatePipelineRunEvent(cde.PipelineRunStartedEventV1, pipelineRunId, 70 | pipelineRunName, pipelineRunStatus, pipelineRunURL, pipelineRunErrors, pipelineRunData) 71 | 72 | event.SetSource(source) 73 | 74 | // Set a target. 75 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 76 | 77 | // Send that Event. 78 | log.Printf("sending event %s\n", event) 79 | 80 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 81 | log.Fatalf("failed to send, %v", result) 82 | return result 83 | } 84 | 85 | return nil 86 | }, 87 | } 88 | 89 | var pipelineRunFinishedCmd = &cobra.Command{ 90 | Use: "finished", 91 | Short: "Emit Pipeline Run Finished Event", 92 | Long: `Emit Pipeline Run Finished CloudEvent`, 93 | RunE: func(cmd *cobra.Command, args []string) error { 94 | c, err := cloudevents.NewDefaultClient() 95 | if err != nil { 96 | log.Fatalf("failed to create client, %v", err) 97 | return err 98 | } 99 | 100 | // Create an Event. 101 | event, _ := cde.CreatePipelineRunEvent(cde.PipelineRunFinishedEventV1, pipelineRunId, 102 | pipelineRunName, pipelineRunStatus, pipelineRunURL, pipelineRunErrors, pipelineRunData) 103 | 104 | event.SetSource(source) 105 | 106 | // Set a target. 107 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 108 | 109 | // Send that Event. 110 | log.Printf("sending event %s\n", event) 111 | 112 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 113 | log.Fatalf("failed to send, %v", result) 114 | return result 115 | } 116 | 117 | return nil 118 | }, 119 | } 120 | 121 | var pipelineRunQueuedCmd = &cobra.Command{ 122 | Use: "queued", 123 | Short: "Emit PipelineRun Queued Event", 124 | Long: `Emit PipelineRun Queued CloudEvent`, 125 | RunE: func(cmd *cobra.Command, args []string) error { 126 | c, err := cloudevents.NewDefaultClient() 127 | if err != nil { 128 | log.Fatalf("failed to create client, %v", err) 129 | return err 130 | } 131 | 132 | // Create an Event. 133 | event, _ := cde.CreatePipelineRunEvent(cde.PipelineRunQueuedEventV1, pipelineRunId, 134 | pipelineRunName, pipelineRunStatus, pipelineRunURL, pipelineRunErrors, pipelineRunData) 135 | 136 | event.SetSource(source) 137 | 138 | // Set a target. 139 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 140 | 141 | // Send that Event. 142 | log.Printf("sending event %s\n", event) 143 | 144 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 145 | log.Fatalf("failed to send, %v", result) 146 | return result 147 | } 148 | 149 | return nil 150 | }, 151 | } 152 | -------------------------------------------------------------------------------- /cde/sdk/go/cmd/repository.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CD Events SDK Authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "context" 20 | "log" 21 | 22 | cde "github.com/cdfoundation/sig-events/cde/sdk/go/pkg/cdf/events" 23 | cloudevents "github.com/cloudevents/sdk-go/v2" 24 | "github.com/spf13/cobra" 25 | ) 26 | 27 | func init() { 28 | rootCmd.AddCommand(repositoryCmd) 29 | repositoryCmd.AddCommand(repositoryCreatedCmd) 30 | repositoryCmd.AddCommand(repositoryModifiedCmd) 31 | repositoryCmd.AddCommand(repositoryDeletedCmd) 32 | 33 | repositoryCmd.PersistentFlags().StringVarP(&repositoryId, "id", "i", "", "Repository Id") 34 | repositoryCmd.PersistentFlags().StringVarP(&repositoryName, "name", "n", "", "Repository's Name") 35 | repositoryCmd.PersistentFlags().StringVarP(&repositoryURL, "url", "u", "", "Repository's URL") 36 | repositoryCmd.PersistentFlags().StringToStringVarP(&repositoryData, "data", "d", map[string]string{}, "Repository's Data") 37 | } 38 | 39 | var repositoryCmd = &cobra.Command{ 40 | Use: "repository", 41 | Short: "Emit Repository related Events", 42 | Long: `Emit Repository related CloudEvent`, 43 | } 44 | 45 | var ( 46 | repositoryId string 47 | repositoryName string 48 | repositoryURL string 49 | repositoryData map[string]string 50 | ) 51 | 52 | var repositoryCreatedCmd = &cobra.Command{ 53 | Use: "created", 54 | Short: "Emit Repository Created Event", 55 | Long: `Emit Repository Created CloudEvent`, 56 | RunE: func(cmd *cobra.Command, args []string) error { 57 | c, err := cloudevents.NewDefaultClient() 58 | if err != nil { 59 | log.Fatalf("failed to create client, %v", err) 60 | return err 61 | } 62 | 63 | // Create an Event. 64 | event, _ := cde.CreateRepositoryEvent(cde.RepositoryCreatedEventV1, repositoryId, 65 | repositoryName, repositoryURL, repositoryData) 66 | 67 | event.SetSource(source) 68 | 69 | // Set a target. 70 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 71 | 72 | // Send that Event. 73 | log.Printf("sending event %s\n", event) 74 | 75 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 76 | log.Fatalf("failed to send, %v", result) 77 | return result 78 | } 79 | 80 | return nil 81 | }, 82 | } 83 | 84 | var repositoryModifiedCmd = &cobra.Command{ 85 | Use: "modified", 86 | Short: "Emit Repository Modified Event", 87 | Long: `Emit Repository Modified CloudEvent`, 88 | RunE: func(cmd *cobra.Command, args []string) error { 89 | c, err := cloudevents.NewDefaultClient() 90 | if err != nil { 91 | log.Fatalf("failed to create client, %v", err) 92 | return err 93 | } 94 | 95 | // Create an Event. 96 | event, _ := cde.CreateRepositoryEvent(cde.RepositoryModifiedEventV1, repositoryId, 97 | repositoryName, repositoryURL, repositoryData) 98 | 99 | event.SetSource(source) 100 | 101 | // Set a target. 102 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 103 | 104 | // Send that Event. 105 | log.Printf("sending event %s\n", event) 106 | 107 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 108 | log.Fatalf("failed to send, %v", result) 109 | return result 110 | } 111 | 112 | return nil 113 | }, 114 | } 115 | 116 | var repositoryDeletedCmd = &cobra.Command{ 117 | Use: "deleted", 118 | Short: "Emit Repository Deleted Event", 119 | Long: `Emit Repository Deleted CloudEvent`, 120 | RunE: func(cmd *cobra.Command, args []string) error { 121 | c, err := cloudevents.NewDefaultClient() 122 | if err != nil { 123 | log.Fatalf("failed to create client, %v", err) 124 | return err 125 | } 126 | 127 | // Create an Event. 128 | event, _ := cde.CreateRepositoryEvent(cde.RepositoryDeletedEventV1, repositoryId, 129 | repositoryName, repositoryURL, repositoryData) 130 | 131 | event.SetSource(source) 132 | 133 | // Set a target. 134 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 135 | 136 | // Send that Event. 137 | log.Printf("sending event %s\n", event) 138 | 139 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 140 | log.Fatalf("failed to send, %v", result) 141 | return result 142 | } 143 | 144 | return nil 145 | }, 146 | } 147 | -------------------------------------------------------------------------------- /cde/sdk/go/cmd/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CD Events SDK Authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package cmd 18 | 19 | import ( 20 | "fmt" 21 | "os" 22 | 23 | homedir "github.com/mitchellh/go-homedir" 24 | "github.com/spf13/cobra" 25 | "github.com/spf13/viper" 26 | ) 27 | 28 | var ( 29 | // Used for flags. 30 | cfgFile string 31 | 32 | rootCmd = &cobra.Command{ 33 | Use: "cde", 34 | Short: "CloudEvents Emitter for Continuous Delivery Events", 35 | Long: `CD - Continuous Delivery is a simple library and CLI to emit CloudEvents related to Continuous Delivery.`, 36 | } 37 | ) 38 | 39 | var CDE_SINK = os.Getenv("CDE_SINK") 40 | var source = "cde-cli" 41 | 42 | // Execute executes the root command. 43 | func Execute() error { 44 | return rootCmd.Execute() 45 | } 46 | 47 | func init() { 48 | 49 | if CDE_SINK == "" { 50 | CDE_SINK = "http://localhost:8080" 51 | } 52 | 53 | cobra.OnInitialize(initConfig) 54 | 55 | rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cde.yaml)") 56 | 57 | } 58 | 59 | func initConfig() { 60 | if cfgFile != "" { 61 | // Use config file from the flag. 62 | viper.SetConfigFile(cfgFile) 63 | } else { 64 | // Find home directory. 65 | home, _ := homedir.Dir() 66 | //cobra.CheckErr(err) 67 | 68 | // Search config in home directory with name ".cobra" (without extension). 69 | viper.AddConfigPath(home) 70 | viper.SetConfigName(".cobra") 71 | } 72 | 73 | viper.AutomaticEnv() 74 | 75 | if err := viper.ReadInConfig(); err == nil { 76 | fmt.Println("Using config file:", viper.ConfigFileUsed()) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /cde/sdk/go/cmd/service.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CD Events SDK Authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "log" 22 | 23 | cde "github.com/cdfoundation/sig-events/cde/sdk/go/pkg/cdf/events" 24 | cloudevents "github.com/cloudevents/sdk-go/v2" 25 | "github.com/spf13/cobra" 26 | ) 27 | 28 | func init() { 29 | rootCmd.AddCommand(serviceCmd) 30 | serviceCmd.AddCommand(serviceDeployedCmd) 31 | serviceCmd.AddCommand(serviceUpgradedCmd) 32 | serviceCmd.AddCommand(serviceRolledbackCmd) 33 | serviceCmd.AddCommand(serviceRemovedCmd) 34 | 35 | serviceCmd.PersistentFlags().StringVarP(&serviceEnvId, "envId", "e", "", "Environment Id where the Service is running") 36 | serviceCmd.PersistentFlags().StringVarP(&serviceName, "name", "n", "", "Service's Name") 37 | serviceCmd.PersistentFlags().StringVarP(&serviceVersion, "version", "v", "", "Service's Version") 38 | serviceCmd.PersistentFlags().StringToStringVarP(&serviceData, "data", "d", map[string]string{}, "Service's Data") 39 | } 40 | 41 | var serviceCmd = &cobra.Command{ 42 | Use: "service", 43 | Short: "Emit Environment related Events", 44 | Long: `Emit Environment related CloudEvent`, 45 | } 46 | 47 | var ( 48 | serviceEnvId string 49 | serviceName string 50 | serviceVersion string 51 | serviceData map[string]string 52 | ) 53 | 54 | var serviceDeployedCmd = &cobra.Command{ 55 | Use: "deployed", 56 | Short: "Emit Service Deployed Event", 57 | Long: `Emit Service Deployed CloudEvent`, 58 | RunE: func(cmd *cobra.Command, args []string) error { 59 | c, err := cloudevents.NewDefaultClient() 60 | if err != nil { 61 | log.Fatalf("failed to create client, %v", err) 62 | return err 63 | } 64 | 65 | // Create an Event. 66 | event, _ := cde.CreateServiceEvent(cde.ServiceDeployedEventV1, serviceEnvId, 67 | serviceName, serviceVersion, serviceData) 68 | 69 | event.SetSource(source) 70 | 71 | // Set a target. 72 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 73 | 74 | // Send that Event. 75 | log.Printf("sending event %s\n", event) 76 | 77 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 78 | log.Fatalf("failed to send, %v", result) 79 | return result 80 | } 81 | 82 | return nil 83 | }, 84 | } 85 | 86 | var serviceUpgradedCmd = &cobra.Command{ 87 | Use: "upgraded", 88 | Short: "Emit Service Upgraded Event", 89 | Long: `Emit Service Upgraded CloudEvent`, 90 | RunE: func(cmd *cobra.Command, args []string) error { 91 | c, err := cloudevents.NewDefaultClient() 92 | if err != nil { 93 | log.Fatalf("failed to create client, %v", err) 94 | return err 95 | } 96 | 97 | // Create an Event. 98 | event, _ := cde.CreateServiceEvent(cde.ServiceUpgradedEventV1, serviceEnvId, 99 | serviceName, serviceVersion, serviceData) 100 | 101 | event.SetSource(source) 102 | 103 | // Set a target. 104 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 105 | 106 | // Send that Event. 107 | log.Printf("sending event %s\n", event) 108 | 109 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 110 | log.Fatalf("failed to send, %v", result) 111 | return result 112 | } 113 | 114 | return nil 115 | }, 116 | } 117 | 118 | var serviceRemovedCmd = &cobra.Command{ 119 | Use: "removed", 120 | Short: "Emit Service Removed Event", 121 | Long: `Emit Service Removed CloudEvent`, 122 | RunE: func(cmd *cobra.Command, args []string) error { 123 | c, err := cloudevents.NewDefaultClient() 124 | if err != nil { 125 | log.Fatalf("failed to create client, %v", err) 126 | return err 127 | } 128 | 129 | // Create an Event. 130 | event, _ := cde.CreateServiceEvent(cde.ServiceRemovedEventV1, serviceEnvId, 131 | serviceName, serviceVersion, serviceData) 132 | 133 | event.SetSource(source) 134 | 135 | // Set a target. 136 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 137 | 138 | // Send that Event. 139 | log.Printf("sending event %s\n", event) 140 | 141 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 142 | log.Fatalf("failed to send, %v", result) 143 | return result 144 | } 145 | 146 | return nil 147 | }, 148 | } 149 | 150 | var serviceRolledbackCmd = &cobra.Command{ 151 | Use: "rolledback", 152 | Short: "Emit Service Rolledback Event", 153 | Long: `Emit Service Rolledback CloudEvent`, 154 | RunE: func(cmd *cobra.Command, args []string) error { 155 | c, err := cloudevents.NewDefaultClient() 156 | if err != nil { 157 | log.Fatalf("failed to create client, %v", err) 158 | return err 159 | } 160 | 161 | // Create an Event. 162 | event, _ := cde.CreateServiceEvent(cde.ServiceRolledbackEventV1, serviceEnvId, 163 | serviceName, serviceVersion, serviceData) 164 | 165 | event.SetSource(source) 166 | 167 | // Set a target. 168 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 169 | 170 | // Send that Event. 171 | log.Printf("sending event %s\n", event) 172 | 173 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 174 | log.Fatalf("failed to send, %v", result) 175 | return result 176 | } 177 | 178 | return nil 179 | }, 180 | } 181 | -------------------------------------------------------------------------------- /cde/sdk/go/cmd/taskrun.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CD Events SDK Authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "log" 22 | 23 | cde "github.com/cdfoundation/sig-events/cde/sdk/go/pkg/cdf/events" 24 | cloudevents "github.com/cloudevents/sdk-go/v2" 25 | "github.com/spf13/cobra" 26 | ) 27 | 28 | func init() { 29 | rootCmd.AddCommand(taskRunCmd) 30 | taskRunCmd.AddCommand(taskRunStartedCmd) 31 | taskRunCmd.AddCommand(taskRunFinishedCmd) 32 | 33 | taskRunCmd.PersistentFlags().StringVarP(&taskRunId, "id", "i", "", "Task Run Id") 34 | taskRunCmd.PersistentFlags().StringVarP(&taskRunName, "name", "n", "", "Task Run's Name") 35 | taskRunCmd.PersistentFlags().StringVarP(&taskRunPipelineId, "pipelineid", "p", "", "Task Run's Pipeline Id") 36 | taskRunCmd.PersistentFlags().StringToStringVarP(&taskRunData, "data", "d", map[string]string{}, "Task Run's Data") 37 | } 38 | 39 | var taskRunCmd = &cobra.Command{ 40 | Use: "taskrun", 41 | Short: "Emit TaskRun related Events", 42 | Long: `Emit TaskRun related CloudEvent`, 43 | } 44 | 45 | var ( 46 | taskRunId string 47 | taskRunName string 48 | taskRunPipelineId string 49 | taskRunData map[string]string 50 | ) 51 | 52 | var taskRunStartedCmd = &cobra.Command{ 53 | Use: "started", 54 | Short: "Emit TaskRun Started Event", 55 | Long: `Emit TaskRun Started CloudEvent`, 56 | RunE: func(cmd *cobra.Command, args []string) error { 57 | c, err := cloudevents.NewDefaultClient() 58 | if err != nil { 59 | log.Fatalf("failed to create client, %v", err) 60 | return err 61 | } 62 | 63 | // Create an Event. 64 | event, _ := cde.CreateTaskRunEvent(cde.TaskRunStartedEventV1, taskRunId, 65 | taskRunName, taskRunPipelineId, taskRunData) 66 | 67 | event.SetSource(source) 68 | 69 | // Set a target. 70 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 71 | 72 | // Send that Event. 73 | log.Printf("sending event %s\n", event) 74 | 75 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 76 | log.Fatalf("failed to send, %v", result) 77 | return result 78 | } 79 | 80 | return nil 81 | }, 82 | } 83 | 84 | var taskRunFinishedCmd = &cobra.Command{ 85 | Use: "finished", 86 | Short: "Emit TaskRun Finished Event", 87 | Long: `Emit TaskRun Finished CloudEvent`, 88 | RunE: func(cmd *cobra.Command, args []string) error { 89 | c, err := cloudevents.NewDefaultClient() 90 | if err != nil { 91 | log.Fatalf("failed to create client, %v", err) 92 | return err 93 | } 94 | 95 | // Create an Event. 96 | event, _ := cde.CreateTaskRunEvent(cde.TaskRunFinishedEventV1, taskRunId, 97 | taskRunName, taskRunPipelineId, taskRunData) 98 | 99 | event.SetSource(source) 100 | 101 | // Set a target. 102 | ctx := cloudevents.ContextWithTarget(context.Background(), CDE_SINK) 103 | 104 | // Send that Event. 105 | log.Printf("sending event %s\n", event) 106 | 107 | if result := c.Send(ctx, event); !cloudevents.IsACK(result) { 108 | log.Fatalf("failed to send, %v", result) 109 | return result 110 | } 111 | 112 | return nil 113 | }, 114 | } 115 | -------------------------------------------------------------------------------- /cde/sdk/go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cdfoundation/sig-events/cde/sdk/go 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/cloudevents/sdk-go/v2 v2.3.1 7 | github.com/mitchellh/go-homedir v1.1.0 8 | github.com/satori/go.uuid v1.2.0 9 | github.com/spf13/cobra v1.1.1 10 | github.com/spf13/viper v1.7.1 11 | ) 12 | -------------------------------------------------------------------------------- /cde/sdk/go/hack/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | RED='\033[0;31m' 6 | GREEN='\033[0;32m' 7 | RESET='\033[0m' 8 | 9 | if ! [ -x "$(command -v goimports)" ]; then 10 | echo "Installing goimports" 11 | go get golang.org/x/tools/cmd/goimports 12 | fi 13 | 14 | echo "Running validation scripts..." 15 | 16 | scripts=( 17 | ) 18 | fail=0 19 | for s in "${scripts[@]}"; do 20 | echo "RUN ${s}" 21 | set +e 22 | $s 23 | result=$? 24 | set -e 25 | if [[ $result -eq 0 ]]; then 26 | echo -e "${GREEN}PASSED${RESET} ${s}" 27 | else 28 | echo -e "${RED}FAILED${RESET} ${s}" 29 | fail=1 30 | fi 31 | done 32 | exit $fail -------------------------------------------------------------------------------- /cde/sdk/go/hack/gofmt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | files=$(find . -name "*.go" | grep -v vendor/ | grep -v ./pkg/client/openapi/all | grep -v Dockerfile | xargs gofmt -l -s) 4 | if [[ $files ]]; then 5 | echo "Gofmt errors in files:" 6 | echo "$files" 7 | diff=$(find . -name "*.go" | grep -v vendor/ | grep -v ./pkg/client/openapi/all | grep -v Dockerfile | xargs gofmt -d -s) 8 | echo "$diff" 9 | exit 1 10 | fi -------------------------------------------------------------------------------- /cde/sdk/go/hack/install_golint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | # Code generated by godownloader on 2018-06-05T12:04:55Z. DO NOT EDIT. 4 | # 5 | 6 | usage() { 7 | this=$1 8 | cat </dev/null 125 | } 126 | echoerr() { 127 | echo "$@" 1>&2 128 | } 129 | log_prefix() { 130 | echo "$0" 131 | } 132 | _logp=6 133 | log_set_priority() { 134 | _logp="$1" 135 | } 136 | log_priority() { 137 | if test -z "$1"; then 138 | echo "$_logp" 139 | return 140 | fi 141 | [ "$1" -le "$_logp" ] 142 | } 143 | log_tag() { 144 | case $1 in 145 | 0) echo "emerg" ;; 146 | 1) echo "alert" ;; 147 | 2) echo "crit" ;; 148 | 3) echo "err" ;; 149 | 4) echo "warning" ;; 150 | 5) echo "notice" ;; 151 | 6) echo "info" ;; 152 | 7) echo "debug" ;; 153 | *) echo "$1" ;; 154 | esac 155 | } 156 | log_debug() { 157 | log_priority 7 || return 0 158 | echoerr "$(log_prefix)" "$(log_tag 7)" "$@" 159 | } 160 | log_info() { 161 | log_priority 6 || return 0 162 | echoerr "$(log_prefix)" "$(log_tag 6)" "$@" 163 | } 164 | log_err() { 165 | log_priority 3 || return 0 166 | echoerr "$(log_prefix)" "$(log_tag 3)" "$@" 167 | } 168 | log_crit() { 169 | log_priority 2 || return 0 170 | echoerr "$(log_prefix)" "$(log_tag 2)" "$@" 171 | } 172 | uname_os() { 173 | os=$(uname -s | tr '[:upper:]' '[:lower:]') 174 | case "$os" in 175 | msys_nt) os="windows" ;; 176 | esac 177 | echo "$os" 178 | } 179 | uname_arch() { 180 | arch=$(uname -m) 181 | case $arch in 182 | x86_64) arch="amd64" ;; 183 | x86) arch="386" ;; 184 | i686) arch="386" ;; 185 | i386) arch="386" ;; 186 | aarch64) arch="arm64" ;; 187 | armv5*) arch="armv5" ;; 188 | armv6*) arch="armv6" ;; 189 | armv7*) arch="armv7" ;; 190 | esac 191 | echo ${arch} 192 | } 193 | uname_os_check() { 194 | os=$(uname_os) 195 | case "$os" in 196 | darwin) return 0 ;; 197 | dragonfly) return 0 ;; 198 | freebsd) return 0 ;; 199 | linux) return 0 ;; 200 | android) return 0 ;; 201 | nacl) return 0 ;; 202 | netbsd) return 0 ;; 203 | openbsd) return 0 ;; 204 | plan9) return 0 ;; 205 | solaris) return 0 ;; 206 | windows) return 0 ;; 207 | esac 208 | log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib" 209 | return 1 210 | } 211 | uname_arch_check() { 212 | arch=$(uname_arch) 213 | case "$arch" in 214 | 386) return 0 ;; 215 | amd64) return 0 ;; 216 | arm64) return 0 ;; 217 | armv5) return 0 ;; 218 | armv6) return 0 ;; 219 | armv7) return 0 ;; 220 | ppc64) return 0 ;; 221 | ppc64le) return 0 ;; 222 | mips) return 0 ;; 223 | mipsle) return 0 ;; 224 | mips64) return 0 ;; 225 | mips64le) return 0 ;; 226 | s390x) return 0 ;; 227 | amd64p32) return 0 ;; 228 | esac 229 | log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib" 230 | return 1 231 | } 232 | untar() { 233 | tarball=$1 234 | case "${tarball}" in 235 | *.tar.gz | *.tgz) tar -xzf "${tarball}" ;; 236 | *.tar) tar -xf "${tarball}" ;; 237 | *.zip) unzip "${tarball}" ;; 238 | *) 239 | log_err "untar unknown archive format for ${tarball}" 240 | return 1 241 | ;; 242 | esac 243 | } 244 | mktmpdir() { 245 | test -z "$TMPDIR" && TMPDIR="$(mktemp -d)" 246 | mkdir -p "${TMPDIR}" 247 | echo "${TMPDIR}" 248 | } 249 | http_download_curl() { 250 | local_file=$1 251 | source_url=$2 252 | header=$3 253 | if [ -z "$header" ]; then 254 | code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url") 255 | else 256 | code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url") 257 | fi 258 | if [ "$code" != "200" ]; then 259 | log_debug "http_download_curl received HTTP status $code" 260 | return 1 261 | fi 262 | return 0 263 | } 264 | http_download_wget() { 265 | local_file=$1 266 | source_url=$2 267 | header=$3 268 | if [ -z "$header" ]; then 269 | wget -q -O "$local_file" "$source_url" 270 | else 271 | wget -q --header "$header" -O "$local_file" "$source_url" 272 | fi 273 | } 274 | http_download() { 275 | log_debug "http_download $2" 276 | if is_command curl; then 277 | http_download_curl "$@" 278 | return 279 | elif is_command wget; then 280 | http_download_wget "$@" 281 | return 282 | fi 283 | log_crit "http_download unable to find wget or curl" 284 | return 1 285 | } 286 | http_copy() { 287 | tmp=$(mktemp) 288 | http_download "${tmp}" "$1" "$2" || return 1 289 | body=$(cat "$tmp") 290 | rm -f "${tmp}" 291 | echo "$body" 292 | } 293 | github_release() { 294 | owner_repo=$1 295 | version=$2 296 | test -z "$version" && version="latest" 297 | giturl="https://github.com/${owner_repo}/releases/${version}" 298 | json=$(http_copy "$giturl" "Accept:application/json") 299 | test -z "$json" && return 1 300 | version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//') 301 | test -z "$version" && return 1 302 | echo "$version" 303 | } 304 | hash_sha256() { 305 | TARGET=${1:-/dev/stdin} 306 | if is_command gsha256sum; then 307 | hash=$(gsha256sum "$TARGET") || return 1 308 | echo "$hash" | cut -d ' ' -f 1 309 | elif is_command sha256sum; then 310 | hash=$(sha256sum "$TARGET") || return 1 311 | echo "$hash" | cut -d ' ' -f 1 312 | elif is_command shasum; then 313 | hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1 314 | echo "$hash" | cut -d ' ' -f 1 315 | elif is_command openssl; then 316 | hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1 317 | echo "$hash" | cut -d ' ' -f a 318 | else 319 | log_crit "hash_sha256 unable to find command to compute sha-256 hash" 320 | return 1 321 | fi 322 | } 323 | hash_sha256_verify() { 324 | TARGET=$1 325 | checksums=$2 326 | if [ -z "$checksums" ]; then 327 | log_err "hash_sha256_verify checksum file not specified in arg2" 328 | return 1 329 | fi 330 | BASENAME=${TARGET##*/} 331 | want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1) 332 | if [ -z "$want" ]; then 333 | log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'" 334 | return 1 335 | fi 336 | got=$(hash_sha256 "$TARGET") 337 | if [ "$want" != "$got" ]; then 338 | log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got" 339 | return 1 340 | fi 341 | } 342 | cat /dev/null <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 -------------------------------------------------------------------------------- /poc/CDF-events-PoC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdfoundation/sig-events/cfe76a50e8a4135fc426633de6ff4cd9ff2cff31/poc/CDF-events-PoC.png -------------------------------------------------------------------------------- /poc/CDF-events-Spinnaker-PoC.drawio: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /poc/CDF-events-Spinnaker-PoC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdfoundation/sig-events/cfe76a50e8a4135fc426633de6ff4cd9ff2cff31/poc/CDF-events-Spinnaker-PoC.png -------------------------------------------------------------------------------- /poc/README.md: -------------------------------------------------------------------------------- 1 | # CDF SIG Events Protocol Proof of Concept 2 | 3 | The SIG Events will advance the [specification of its protocol](https://github.com/cdfoundation/sig-events/blob/main/vocabulary-draft/introduction.md) (a.k.a. "CD Events") 4 | and the development of the SDK with the help a proof of concept (PoC). 5 | 6 | The PoC and related material will help raising questions and motivating 7 | decisions; it will be beneficial to the SIG in presentations about the 8 | work that the SIG Events is doing. 9 | 10 | ## PoC Overview 11 | 12 | The proof of concept showcases interaction between [Tekton](https://tekton.dev) 13 | and [Keptn](https://keptn.sh/), using the CD Events specification being worked on by 14 | the [Events Special Interest Group](https://github.com/cdfoundation/sig-events) 15 | part of the [Continuous Delivery Foundation](https://cd.foundation/) (CDF). 16 | 17 | Tekton is an open-source framework for creating CI/CD systems, and is a CDF 18 | founding project. 19 | 20 | Keptn is an open-source cloud-native application life-cycle orchestration 21 | solution and is a [Cloud-Native Computing Foundation](https://www.cncf.io/) 22 | Sandbox project. 23 | 24 | The PoC uses the CDF SIG Events Protocol [Go library/CLI](https://github.com/cdfoundation/sig-events/tree/main/cde/sdk/go) 25 | to produce and send events. 26 | 27 | ### Purpose of the PoC 28 | 29 | The two primary purposes of the PoC is one, as mentioned above, 30 | to help the work of SIG Events, and two, to show an indication of the 31 | power of having individually strong projects such as Tekton and Keptn 32 | collaborating to provide even more value. 33 | 34 | In the PoC some shortcuts will be taken, for instance there will be no 35 | true native communication between Tekton and Keptn, some translation 36 | layers will be used. It is important to note, however, that the PoC is 37 | being developed with active participation from the Tekton and Keptn 38 | communities, so native communication is already on the agenda for 39 | post-PoC work. 40 | 41 | ### PoC Use Case 42 | 43 | The use case showcased by the PoC can be summarized as follows: 44 | 45 | 1. A new version of a container is built by Tekton. 46 | 2. Keptn is informed by this new build. 47 | 3. Keptn decides what to do next following its orchestration manifest. 48 | 4. Keptn sends out a request for the next operation to be started. 49 | (In the PoC, the next operation will be deployment of the container.) 50 | 5. Tekton recieves the request and runs the deployment operation. 51 | 6. Keptn is informed that the operation is completed. 52 | 53 | ### PoC modules and interactions 54 | 55 | An overview of the modules and interactions can be seen in the following diagram: 56 | 57 | ![poc diagram](CDF-events-PoC.png "PoC Diagram") 58 | 59 | Going from top to bottom in the diagram: 60 | 61 | * At the top, we have **Tekton** without modifications. 62 | Tekton runs the build and deploy operations for the described use case above. 63 | * Just below, we have an experimental **[Tekton CloudEvents controller](https://github.com/tektoncd/experimental/tree/main/cloudevents)**. 64 | The controller creates CD Events from Tekton internal signals. 65 | * Next, we have the **CD Events Go library/CLI** which provides both the encoding 66 | and transmission of CD Events following the specification. 67 | * The Go library/CLI internally uses the **[CloudEvents Go SDK](https://github.com/cloudevents/sdk-go)** 68 | to create and send events. 69 | * Next to Keptn, we have two modules, one for incoming and one for outgoing Keptn events. 70 | * The **[Keptn translation](https://github.com/salaboy/keptn-cdf-translator)** module translates from CD Events 71 | to Keptn native events. 72 | * The **[Keptn service](https://github.com/salaboy/cdf-events-keptn-adapter)** module creates CD Events from Keptn internal signals. 73 | * Finally, we have **Keptn** itself, orchestrating the life-cycle operations for the use case above. 74 | 75 | Detailed sequence diagram: 76 | ![poc sequence diagram](sig-events-sequence.png "PoC Sequence Diagram") 77 | 78 | Detailed functional diagram: 79 | 80 | ![poc functional diagram](sig-events-functional.png "PoC Functional Diagram") 81 | -------------------------------------------------------------------------------- /poc/cloudplayer/deploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: serving.knative.dev/v1 2 | kind: Service 3 | metadata: 4 | name: cloudevents-player 5 | spec: 6 | template: 7 | metadata: 8 | annotations: 9 | autoscaling.knative.dev/minScale: "1" 10 | spec: 11 | containers: 12 | - image: quay.io/ruben/cloudevents-player:latest 13 | env: 14 | - name: PLAYER_MODE 15 | value: KNATIVE 16 | - name: PLAYER_BROKER 17 | value: default 18 | --- 19 | apiVersion: eventing.knative.dev/v1 20 | kind: Trigger 21 | metadata: 22 | name: cloudevents-player 23 | annotations: 24 | knative-eventing-injection: enabled 25 | spec: 26 | broker: events-broker 27 | subscriber: 28 | ref: 29 | apiVersion: serving.knative.dev/v1 30 | kind: Service 31 | name: cloudevents-player 32 | -------------------------------------------------------------------------------- /poc/demo-script.md: -------------------------------------------------------------------------------- 1 | # SIG Events Proof of Concept - Demo Script 2 | 3 | This document is a walk-through running the PoC, from setting up the environment, 4 | then step-by-step how to run the demo. 5 | 6 | ## Installation 7 | 8 | From the `poc` folder, run the poc script. 9 | It will create a kind cluster, install and configure all required components. 10 | The script prerequisites and details are documented within the script itself. 11 | 12 | ```shell 13 | ./poc.sh 14 | ``` 15 | 16 | Login to the [keptn bridge](https://keptn-127.0.0.1.nip.io/bridge/) with the credentials 17 | generated by `keptn configure bridge -o`. The self-signed certificate is not trusted by the 18 | web-browser, ignore the warning and continue to the bridge. 19 | 20 | ## Running the demo 21 | 22 | To kick-off the demo, start the build Tekton pipeline from the `poc` folder: 23 | 24 | ```shell 25 | tkn pipeline start build-artifact -w name=sources,volumeClaimTemplateFile=./tekton/workspace-template.yaml 26 | ``` 27 | 28 | - Watch the pipeline execution in the [Tekton Dashboard](http://localhost/dashboard) 29 | - Watch the Keptn sequence progress on the [bridge](https://keptn-127.0.0.1.nip.io/bridge/project/cde/sequence) 30 | - Once the sequence is complete the [link](http://localhost/poc/) in the deployment on the bridge opens the deployed demo app 31 | 32 | ## Cleanup 33 | 34 | Recreate the Keptn project and delete all Tekton `PipelineRuns`: 35 | 36 | ```shell 37 | keptn delete project cde 38 | keptn create project cde --shipyard=resources/shipyard.yaml 39 | keptn create service poc --project cde 40 | tkn pr delete --all 41 | tkn pr delete --all -n cdevents 42 | ``` -------------------------------------------------------------------------------- /poc/docker/CDF-events-PoC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdfoundation/sig-events/cfe76a50e8a4135fc426633de6ff4cd9ff2cff31/poc/docker/CDF-events-PoC.png -------------------------------------------------------------------------------- /poc/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:latest 2 | COPY ./index.html /usr/share/nginx/html/index.html 3 | COPY ./CDF-events-PoC.png usr/share/nginx/html/CDF-events-PoC.png -------------------------------------------------------------------------------- /poc/docker/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CDF SIG Events 5 | 6 | 7 |

Hello, KubeCon + CloudNativeCon NA 2021!

8 |

This page was built and deployed by Tekton and Keptn, working together through CD Events

9 |

Diagram

10 | 11 | 12 | -------------------------------------------------------------------------------- /poc/poc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e -o pipefail 3 | 4 | BASE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 5 | 6 | declare TEKTON_PIPELINE_VERSION TEKTON_TRIGGERS_VERSION TEKTON_DASHBOARD_VERSION TEKTON_CLOUDEVENTS_PATH 7 | declare KNATIVE_VERSION KNATIVE_NET_CONTOUR_VERSION KNATIVE_EVENTING_VERSION 8 | declare KEPTN_CDF_TRANSLATOR_PATH CDF_EVENTS_KEPTN_ADAPTER_PATH KEPTN_PROJECT KEPTN_SERVICE 9 | declare BROKER_NAME 10 | 11 | export KO_DOCKER_REPO=kind.local 12 | 13 | # This script deploys all the software components required for the PoC 14 | # on a local laptop running docker. 15 | # It creates a kind cluster named cde and deploys to it: 16 | # - Tekton 17 | # - Tekton experiment cloudevents controller 18 | # - Keptn (v0.9.x+) 19 | # - Keptn inbound translation layer 20 | # - Keptn output adapter layer 21 | # - Knative + Courier Ingress 22 | # It deploys a container registry available at localhost:5000 and reachable from 23 | # the kind cluster. 24 | 25 | # Prerequisites: 26 | # - go 1.15+ 27 | # - docker (recommended 8GB memory config) 28 | # - ko (https://github.com/google/ko) 29 | # - kind 0.11.1 (https://kind.sigs.k8s.io/docs/user/quick-start/) 30 | # - tkn CLI (https://github.com/tektoncd/cli) 31 | # - keptn CLI (`curl -sL https://get.keptn.sh | bash`) 32 | # - keptn inbound/outbound adapters and tekton cloudevent controller 33 | # https://github.com/salaboy/cdf-events-keptn-adapter - inbound 34 | # https://github.com/salaboy/keptn-cdf-translator - outbound 35 | # https://github.com/tektoncd/experimental - contains tekton cloudevent controller 36 | # should be cloned under $GOROOT/src/github.com// or alternatively 37 | # the corresponding PATH environment variables must be set (see the declare section above) 38 | # - Spin CLI (https://spinnaker.io/docs/setup/other_config/spin/) 39 | 40 | 41 | # Notes: 42 | # - Latest versions will be installed if not specified 43 | # - The script is written to be mostly idempotent 44 | 45 | RUN_WITH_SPINNAKER="false" 46 | 47 | get_latest_release() { 48 | curl --silent "https://api.github.com/repos/$1/releases/latest" | # Get latest release from GitHub api 49 | grep '"tag_name":' | # Get tag line 50 | sed -E 's/.*"([^"]+)".*/\1/' # Pluck JSON value 51 | } 52 | 53 | retry_command() { 54 | for counter in {1..5}; do 55 | $1 && break 56 | sleep $((counter + 1)) 57 | [[ $counter -eq 5 ]] && echo "Failed!" && exit 1 58 | echo "Trying again. Try #$counter" 59 | done 60 | } 61 | 62 | # Read command line options 63 | while getopts ":c:p:t:d:k:sh" opt; do 64 | case ${opt} in 65 | c ) 66 | CLUSTER_NAME=$OPTARG 67 | ;; 68 | p ) 69 | TEKTON_PIPELINE_VERSION=$OPTARG 70 | ;; 71 | t ) 72 | TEKTON_TRIGGERS_VERSION=$OPTARG 73 | ;; 74 | d ) 75 | TEKTON_DASHBOARD_VERSION=$OPTARG 76 | ;; 77 | k ) 78 | KNATIVE_VERSION=$OPTARG 79 | ;; 80 | s ) 81 | RUN_WITH_SPINNAKER="true" 82 | ;; 83 | h ) 84 | echo "Usage: poc.sh [-c cluster-name -p pipeline-version -t triggers-version -d dashboard-version -k knative-version -s ]" 85 | exit 0 86 | ;; 87 | \? ) 88 | echo "Invalid option: $OPTARG" 1>&2 89 | echo 1>&2 90 | echo "Usage: poc.sh [-c cluster-name -p pipeline-version -t triggers-version -d dashboard-version -k knative-version -s ]" 91 | ;; 92 | : ) 93 | echo "Invalid option: $OPTARG requires an argument" 1>&2 94 | echo 1>&2 95 | echo "Usage: poc.sh [-c cluster-name -p pipeline-version -t triggers-version -d dashboard-version -k knative-version -s ]" 96 | ;; 97 | esac 98 | done 99 | shift $((OPTIND -1)) 100 | 101 | # Check and defaults input params 102 | export KIND_CLUSTER_NAME=${CLUSTER_NAME:-"cde"} 103 | 104 | if [ -z "$TEKTON_PIPELINE_VERSION" ]; then 105 | TEKTON_PIPELINE_VERSION=$(get_latest_release tektoncd/pipeline) 106 | fi 107 | if [ -z "$TEKTON_TRIGGERS_VERSION" ]; then 108 | TEKTON_TRIGGERS_VERSION=$(get_latest_release tektoncd/triggers) 109 | fi 110 | if [ -z "$TEKTON_DASHBOARD_VERSION" ]; then 111 | TEKTON_DASHBOARD_VERSION=$(get_latest_release tektoncd/dashboard) 112 | fi 113 | 114 | KNATIVE_VERSION=${KNATIVE_VERSION:-1.3.0} 115 | 116 | echo "Checking if needed repos can be found" 117 | if [ ! -d "${KEPTN_CDF_TRANSLATOR_PATH:-${GOPATH}/src/github.com/salaboy/keptn-cdf-translator}" ] 118 | then 119 | echo "Can not find required repo: github.com/salaboy/keptn-cdf-translator" 120 | exit 1 # die with error code 1 121 | fi 122 | 123 | if [ ! -d "${CDF_EVENTS_KEPTN_ADAPTER_PATH:-${GOPATH}/src/github.com/salaboy/cdf-events-keptn-adapter}" ] 124 | then 125 | echo "Can not find required repo: github.com/salaboy/cdf-events-keptn-adapter" 126 | exit 1 # die with error code 1 127 | fi 128 | 129 | if [ ! -d "${TEKTON_CLOUDEVENTS_PATH:-${GOPATH}/src/github.com/tektoncd/experimental/cloudevents}" ] 130 | then 131 | echo "Can not find required repo: github.com/tektoncd/experimental/cloudevents" 132 | exit 1 # die with error code 1 133 | fi 134 | 135 | # As we have "set -e" we only need to run the commands. If not found the script will exit. 136 | echo "Checking if needed commands can be found" 137 | go version > /dev/null 138 | ko version > /dev/null 139 | kind version > /dev/null 140 | keptn > /dev/null 141 | if [[ $RUN_WITH_SPINNAKER == "true" ]]; then 142 | spin --version >> /dev/null 143 | fi 144 | 145 | echo "===> Creating a local Container Registry" 146 | # create registry container unless it already exists 147 | reg_name='kind-registry' 148 | reg_port='5000' 149 | running="$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" 150 | if [ "${running}" != 'true' ]; then 151 | docker run \ 152 | -d --restart=always -p "${reg_port}:5000" --name "${reg_name}" \ 153 | registry:2 154 | fi 155 | echo 156 | echo "Accessing your local container registry:" 157 | echo "- From you local laptop: localhost:5000 (docker tag localhost:5000/path/to/)" 158 | echo "- From within the cluster:" 159 | echo " - The container runtime can pull images from localhost:5000" 160 | echo " - The nodes / containers can reach the registry at kind-registry:5000" 161 | echo " Since it's not localhost, most tools will assume https. Use an \"--insecure\" flag if available" 162 | echo 163 | 164 | echo "===> Creating a Kind Cluster (1 control plan, 2 worker nodes" 165 | # Create the kind cluster 166 | # create a cluster with the local registry enabled in containerd 167 | running_cluster=$(kind get clusters | grep "$KIND_CLUSTER_NAME" || true) 168 | if [ "${running_cluster}" != "$KIND_CLUSTER_NAME" ]; then 169 | cat < /dev/null 203 | 204 | # connect the registry to the cluster network 205 | # (the network may already be connected) 206 | docker network connect "kind" "${reg_name}" || true 207 | 208 | # Knative (serving only for now) 209 | echo "===> Deploying Knative controller" 210 | export KNATIVE_VERSION 211 | curl -sL https://raw.githubusercontent.com/csantanapr/knative-kind/master/02-serving.sh | bash 212 | 213 | echo "===> Deploying Contour" 214 | export KNATIVE_NET_CONTOUR_VERSION=${KNATIVE_NET_CONTOUR_VERSION:-$KNATIVE_VERSION} 215 | curl -sL https://raw.githubusercontent.com/csantanapr/knative-kind/master/02-contour.sh | bash 216 | 217 | echo "===> Deploying Knative Serving" 218 | export KNATIVE_EVENTING_VERSION=${KNATIVE_EVENTING_VERSION:-$KNATIVE_VERSION} 219 | export BROKER_NAME=${BROKER_NAME:-"events-broker"} 220 | kubectl delete "broker/${BROKER_NAME}" > /dev/null || true 221 | curl -sL https://raw.githubusercontent.com/csantanapr/knative-kind/master/04-eventing.sh | bash 222 | 223 | EXTERNAL_IP="127.0.0.1" 224 | KNATIVE_DOMAIN="knative-$EXTERNAL_IP.nip.io" 225 | kubectl patch configmap -n knative-serving config-domain -p "{\"data\": {\"$KNATIVE_DOMAIN\": \"\"}}" 226 | 227 | echo "===> RBAC and secrets" 228 | # Install some basic RBAC and secrets needed by triggers 229 | kubectl create -f tekton/rbac.yaml || true 230 | 231 | echo "===> Install Tekton" 232 | 233 | kubectl create namespace --save-config tekton-pipelines || true 234 | 235 | # Deploy an ingress for the Tekton Dashboard 236 | cat < /dev/null 264 | kubectl apply -f "https://storage.googleapis.com/tekton-releases/triggers/previous/${TEKTON_TRIGGERS_VERSION}/release.yaml" > /dev/null 265 | kubectl wait --for condition=established --timeout=60s crd -l app.kubernetes.io/part-of=tekton-triggers 266 | kubectl apply -f "https://storage.googleapis.com/tekton-releases/triggers/previous/${TEKTON_TRIGGERS_VERSION}/interceptors.yaml" > /dev/null 267 | kubectl apply -f "https://github.com/tektoncd/dashboard/releases/download/${TEKTON_DASHBOARD_VERSION}/tekton-dashboard-release.yaml" > /dev/null 268 | # Wait until all pods are ready 269 | kubectl wait -n tekton-pipelines --for=condition=ready pods --all --timeout=120s 270 | 271 | # Configure Tekton 272 | retry_command "kubectl patch cm feature-flags -n tekton-pipelines -p {\"data\":{\"enable-tekton-oci-bundles\":\"true\"}}" 273 | kubectl create namespace production || true 274 | 275 | # Install keptn 276 | 277 | keptn install --yes -q > /dev/null 278 | 279 | # Deploy an ingress for Keptn Bridge and API 280 | kubectl create -f - < /dev/null || true 311 | keptn create project "$KEPTN_PROJECT" --shipyard="$BASE_DIR/resources/shipyard.yaml" 312 | keptn create service "$KEPTN_SERVICE" --project="$KEPTN_PROJECT" 313 | 314 | echo "To obtain bridge credentials, run:" 315 | echo "keptn configure bridge -o" 316 | 317 | # Install the keptn inbound and outbound adapters 318 | export GO111MODULE=on 319 | 320 | echo "===> Install Keptn Inboud" 321 | KEPTN_CDF_TRANSLATOR_PATH=${KEPTN_CDF_TRANSLATOR_PATH:-${GOPATH}/src/github.com/salaboy/keptn-cdf-translator} 322 | pushd "$KEPTN_CDF_TRANSLATOR_PATH" 323 | ko apply -f config/ 324 | popd 325 | 326 | echo "===> Install Keptn Outbound" 327 | CDF_EVENTS_KEPTN_ADAPTER_PATH=${CDF_EVENTS_KEPTN_ADAPTER_PATH:-${GOPATH}/src/github.com/salaboy/cdf-events-keptn-adapter} 328 | pushd "$CDF_EVENTS_KEPTN_ADAPTER_PATH" 329 | docker build --tag localhost:${reg_port}/cdevents/cdf-events-keptn-adapter:latest . 330 | docker push localhost:${reg_port}/cdevents/cdf-events-keptn-adapter:latest 331 | kubectl apply -f deploy/service.yaml 332 | # Set the correct pub-sub topics 333 | kubectl set env deployment/cdf-events-keptn-adapter -n keptn PUBSUB_TOPIC=sh.keptn.event.deployment.triggered -c distributor 334 | pushd 335 | 336 | echo "===> Install Tekton CloudEvents controller" 337 | # Install the Tekton cloudevents experimental controller 338 | TEKTON_CLOUDEVENTS_PATH=${TEKTON_CLOUDEVENTS_PATH:-${GOPATH}/src/github.com/tektoncd/experimental/cloudevents} 339 | pushd "$TEKTON_CLOUDEVENTS_PATH" 340 | ko apply -f config/ 341 | popd 342 | 343 | # Configure the cloudevents controller to talk to the keptn inbound adapter 344 | BROKER_SINK="http://broker-ingress.knative-eventing.svc.cluster.local/default/$BROKER_NAME" 345 | kubectl patch cm config-defaults -n tekton-cloudevents -p '{"data": {"default-cloud-events-sink": "'$BROKER_SINK'"}}' 346 | 347 | # Install Tekton Resources 348 | kubectl create -f "$BASE_DIR/tekton/resources/" || true 349 | 350 | # Install cloud-player 351 | kubectl create -f "$BASE_DIR/cloudplayer/deploy.yaml" 352 | 353 | # Create the triggers to get the events to the keptn inbound service 354 | kubectl create -f - < poc.env 416 | 417 | # Echo endpoints and demo 418 | echo "Tekton Dashboard available at http://tekton-127.0.0.1.nip.io" 419 | echo "Keptn Bridge available at http://keptn-127.0.0.1.nip.io" 420 | echo "-> for the login creds, use keptn configure bridge -o" 421 | echo "CloudEvents player available at http://cloudevents-player.default.knative-127.0.0.1.nip.io" 422 | if [[ $RUN_WITH_SPINNAKER == "true" ]]; then 423 | echo "Spinnaker UI available at http://spin-ui-127.0.0.1.nip.io" 424 | fi 425 | 426 | echo "To kick off the demo, from the poc folder, run tkn:" 427 | echo "tkn pipeline start build-artifact -w name=sources,volumeClaimTemplateFile=./tekton/workspace-template.yaml" 428 | -------------------------------------------------------------------------------- /poc/resources/README.md: -------------------------------------------------------------------------------- 1 | This PoC was built following the Keptn Quickstart into K3s: https://keptn.sh/docs/quickstart/ 2 | 3 | If you have keptn cli installed already you can run these two commands: 4 | 5 | 1) `k3d cluster create mykeptn -p "8082:80@agent[0]" --k3s-server-arg '--no-deploy=traefik' --agents 1` 6 | 2) `keptn install --use-case=continuous-delivery` 7 | 8 | And instead of creating a project with the quickstart instructions the shipyard.yaml file is provided to run the following steps. 9 | 10 | You need to export the two following Environment Variables: 11 | 12 | **Note**: If you don't install istio for ingress you need to port-forward with: 13 | `kubectl -n keptn port-forward service/api-gateway-nginx 8080:80` 14 | 15 | 1) `export KEPTN_ENDPOINT=http://localhost:8080/api` I 16 | 17 | 2) `export KEPTN_API_TOKEN=$(kubectl get secret keptn-api-token -n keptn -ojsonpath={.data.keptn-api-token} | base64 --decode) 18 | 19 | Then run 20 | `keptn auth --endpoint=$KEPTN_ENDPOINT --api-token=$KEPTN_API_TOKEN` 21 | 22 | Then configure the bridge (this will give you the credentials for the user interface located at http://localhost:8080) 23 | `keptn configure bridge -o` 24 | 25 | Run the CLI Command: `keptn create project fmtok8s --shipyard=your_shipyard.yaml` 26 | 27 | Run the CLI command: `keptn create service podtato-server --project=fmtok8s` 28 | 29 | Now you can start the CDEvents to Ketpn Inbound Translator component () with from inside the translator go project (this will configure a server in port 8081 to avoid conflict with Keptn in 8080): 30 | 31 | ``` 32 | go build 33 | go run main.go 34 | ``` 35 | 36 | You can also run the translator component in the k8s cluster: 37 | ```bash 38 | export KO_DOCKER_REPO= 39 | ko apply -f config/ 40 | ``` 41 | 42 | Using the CDE binary you can emit an CD CloudEvent to the translator layer. Make sure that you export the CDE_SINK for CDE to point to the translator endpoint: 43 | `export CDE_SINK=http://localhost:8081/events` 44 | 45 | `cde artifact packaged --id agenda-service-rest --name agenda-service -v 0.0.20 --data artifact=hash` 46 | 47 | This will hit the translator layer which will create a Keptn Event to trigger the Keptn Delivery Sequence (specified in the shipyard file) 48 | 49 | Inside Keptn we need to have an outbound event translator to CD Events (https://github.com/salaboy/cdf-events-keptn-adapter). This component picks events emitted by Keptn, transform them into CD Events and send them to a configurable SINK. 50 | 51 | For this to work, you need to make sure that the Helm Service in Keptn which is automatically configured when Keptn is installed is not listening to delivery events. You can do that by editing the `helm-service` deployment in the `keptn` namespace and remove the value: `sh.keptn.event.deployment.triggered` from the `PUBSUB_TOPIC` env variable. 52 | 53 | You need to make sure that the outbound service deployed in Keptn is listening to this event: 54 | 55 | ``` 56 | - name: PUBSUB_TOPIC 57 | value: sh.keptn.event.deployment.triggered 58 | ``` 59 | 60 | Note: make sure that you remove the following variable for the routing to work: 61 | ``` 62 | - name: PUBSUB_URL 63 | value: nats://keptn-nats-cluster 64 | ``` 65 | 66 | -------------------------------------------------------------------------------- /poc/resources/ce.json: -------------------------------------------------------------------------------- 1 | { 2 | "specversion": "1.0", 3 | "id": "8e273904-d0d6-11eb-a902-784f43552794", 4 | "source": "keptn-cdf-translator", 5 | "type": "sh.keptn.event.production.delivery.triggered", 6 | "datacontenttype": "application/json", 7 | "time": "2021-06-19T08:15:49.144604Z", 8 | "data": { 9 | "project": "fmtok8s", 10 | "stage": "production", 11 | "service": "agenda-service", 12 | "configurationChange": { 13 | "values": { 14 | "image": "docker.io/salaboy/agenda-service:0.0.20" 15 | } 16 | }, 17 | "deployment": { 18 | "deploymentURIsLocal": null, 19 | "deploymentstrategy": "" 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /poc/resources/shipyard.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "spec.keptn.sh/0.2.2" 2 | kind: "Shipyard" 3 | metadata: 4 | name: "cde-stages" 5 | spec: 6 | stages: 7 | - name: "production" 8 | sequences: 9 | - name: "delivery" 10 | tasks: 11 | - name: "approval" 12 | properties: 13 | pass: "manual" 14 | warning: "manual" 15 | - name: "deployment" 16 | properties: 17 | deploymentstrategy: "direct" 18 | -------------------------------------------------------------------------------- /poc/sequence-spin-cdevents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdfoundation/sig-events/cfe76a50e8a4135fc426633de6ff4cd9ff2cff31/poc/sequence-spin-cdevents.png -------------------------------------------------------------------------------- /poc/sequence-spin-cdevents.txt: -------------------------------------------------------------------------------- 1 | @startuml 2 | User -> Tekton: Start Build Pipeline 3 | Tekton --> KeptnInbound: cd.pipeline.start 4 | Tekton -> Tekton: Run Build Pipeline 5 | Tekton -> KeptnInbound: cd.artifact.packaged 6 | Note right of Tekton: (ID, Name, Version) 7 | KeptnInbound -> Keptn: prod.delivery.triggered 8 | Note right of KeptnInbound: (Project, Stage, Service, Config Change) 9 | Note left of Keptn: Name -> Service, ID -> Config Change 10 | Keptn -> User: Wait for Approval 11 | Note left of User: Config Change 12 | User -> Keptn: Approved! 13 | Keptn -> KeptnOutbound: deployment.triggered 14 | Note right of Keptn: (Project, Service, Stage, Id, Ctx) 15 | Note right of Keptn: The Keptn Ctx identifies the Deployment Task 16 | KeptnOutbound -> TektonTriggers: cd.artifact.published 17 | Note right of KeptnOutbound: (ID, Name, Image, TriggerId, Ctx) 18 | Note left of TektonTriggers: TriggerId = Id from deployment, ID and Image -> Config Change, Name -> Service 19 | KeptnOutbound -> Spinnaker: cd.artifact.published 20 | Note right of KeptnOutbound: (ID, Name, Image, TriggerId, Ctx) 21 | KeptnOutbound -> KeptnInbound: cd.artifact.published 22 | Note left of KeptnOutbound: (ID, Name, Image, TriggerId, Ctx) 23 | KeptnInbound -> Keptn: deployment.started 24 | Note right of KeptnInbound: (Project, Stage, Service) 25 | TektonTriggers -> Tekton: start pipeline 26 | Note left of TektonTriggers: (ArtifactID, ArtifactName, TriggerId, Ctx) 27 | Tekton -> Tekton: Run Deploy Pipeline 28 | Tekton -> KeptnInbound: cd.service.deployed 29 | Note right of Tekton: (EnvID, ServiceName, Version, TriggerId, Ctx) 30 | Spinnaker -> Spinnaker: Run Deploy Pipeline 31 | Spinnaker -> KeptnInbound: cd.service.deployed 32 | Note left of Spinnaker: (EnvID, ServiceName, Version, TriggerId, Ctx) 33 | Note left of KeptnInbound: EnvId and ServiceName -> Name/Service, Version = Deployment Gen 34 | KeptnInbound -> Keptn: deployment.finished 35 | Note right of KeptnInbound: (Project, Stage, Service, URI, Name, TriggerId, Ctx) 36 | @enduml 37 | -------------------------------------------------------------------------------- /poc/sequence.txt: -------------------------------------------------------------------------------- 1 | sequenceDiagram 2 | User ->>+Tekton: Start Build Pipeline 3 | Tekton --) Keptn Inbound: cd.pipeline.start 4 | Tekton -->> -Tekton: Run Build Pipeline 5 | Tekton -) Keptn Inbound: cd.artifact.packaged 6 | Note right of Tekton: (ID, Name, Version) 7 | Keptn Inbound -) Keptn: prod.delivery.triggered 8 | Note right of Keptn Inbound: (Project, Stage, Service, Config Change) 9 | Note left of Keptn: Name -> Service, ID -> Config Change 10 | Keptn ->> User: Wait for Approval 11 | Note left of User: Config Change 12 | User ->> Keptn: Approved! 13 | Keptn -) Keptn Outbound: deployment.triggered 14 | Note right of Keptn: (Project, Service, Stage, Id, Ctx) 15 | Note right of Keptn: The Keptn Ctx identifies the Deployment Task 16 | Keptn Outbound -) Tekton Triggers: cd.artifact.published 17 | Note right of Keptn Outbound: (ID, Name, Image, TriggerId, Ctx) 18 | Note left of Tekton Triggers: TriggerId = Id from deployment, ID and Image -> Config Change, Name -> Service 19 | Keptn Outbound -) Keptn Inbound: cd.artifact.published 20 | Note left of Keptn Outbound: (ID, Name, Image, TriggerId, Ctx) 21 | Keptn Inbound -) Keptn: deployment.started 22 | Note right of Keptn Inbound: (Project, Stage, Service) 23 | Tekton Triggers ->> +Tekton: start pipeline 24 | Note left of Tekton Triggers: (ArtifactID, ArtifactName, TriggerId, Ctx) 25 | Tekton ->> -Tekton: Run Deploy Pipeline 26 | Tekton -) Keptn Inbound: cd.service.deployed 27 | Note right of Tekton: (EnvID, ServiceName, Version, TriggerId, Ctx) 28 | Note left of Keptn Inbound: EnvId and ServiceName -> Name/Service, Version = Deployment Gen 29 | Keptn Inbound -) Keptn: deployment.finished 30 | Note right of Keptn Inbound: (Project, Stage, Service, URI, Name, TriggerId, Ctx) -------------------------------------------------------------------------------- /poc/sig-events-functional.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdfoundation/sig-events/cfe76a50e8a4135fc426633de6ff4cd9ff2cff31/poc/sig-events-functional.png -------------------------------------------------------------------------------- /poc/sig-events-sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdfoundation/sig-events/cfe76a50e8a4135fc426633de6ff4cd9ff2cff31/poc/sig-events-sequence.png -------------------------------------------------------------------------------- /poc/sig-events-spinnaker-functional.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdfoundation/sig-events/cfe76a50e8a4135fc426633de6ff4cd9ff2cff31/poc/sig-events-spinnaker-functional.png -------------------------------------------------------------------------------- /poc/spinnaker/deploy-spinnaker-poc.json: -------------------------------------------------------------------------------- 1 | { 2 | "isNew": true, 3 | "application": "cdevents-poc", 4 | "keepWaitingPipelines": false, 5 | "lastModifiedBy": "anonymous", 6 | "limitConcurrent": true, 7 | "name": "deploy-spinnaker-poc", 8 | "spelEvaluator": "v4", 9 | "stages": [ 10 | { 11 | "account": "poc-k8s-account", 12 | "cloudProvider": "kubernetes", 13 | "manifests": [ 14 | { 15 | "apiVersion": "apps/v1", 16 | "kind": "Deployment", 17 | "metadata": { 18 | "name": "spinnaker-poc-deployment", 19 | "namespace": "default" 20 | }, 21 | "spec": { 22 | "replicas": 1, 23 | "selector": { 24 | "matchLabels": { 25 | "app": "cdevents-spinnaker-poc" 26 | } 27 | }, 28 | "template": { 29 | "metadata": { 30 | "labels": { 31 | "app": "cdevents-spinnaker-poc" 32 | } 33 | }, 34 | "spec": { 35 | "containers": [ 36 | { 37 | "image": "localhost:5000/cdevent/poc:v1.0", 38 | "name": "cdevents-spinnaker-poc", 39 | "ports": [ 40 | { 41 | "containerPort": 80 42 | } 43 | ] 44 | } 45 | ] 46 | } 47 | } 48 | } 49 | }, 50 | { 51 | "apiVersion": "v1", 52 | "kind": "Service", 53 | "metadata": { 54 | "name": "spinnaker-poc-service", 55 | "namespace": "default" 56 | }, 57 | "spec": { 58 | "ports": [ 59 | { 60 | "port": 80, 61 | "targetPort": 80 62 | } 63 | ], 64 | "selector": { 65 | "app": "cdevents-spinnaker-poc" 66 | }, 67 | "type": "ClusterIP" 68 | } 69 | } 70 | ], 71 | "moniker": { 72 | "app": "cdevents-poc" 73 | }, 74 | "name": "Deploy (Manifest)", 75 | "refId": "1", 76 | "requisiteStageRefIds": [], 77 | "skipExpressionEvaluation": false, 78 | "source": "text", 79 | "trafficManagement": { 80 | "enabled": false, 81 | "options": { 82 | "enableTraffic": false, 83 | "services": [] 84 | } 85 | }, 86 | "type": "deployManifest" 87 | } 88 | ], 89 | "triggers": [], 90 | "updateTs": "1649334445000" 91 | } 92 | -------------------------------------------------------------------------------- /poc/spinnaker/deploySpinnakerWithHalyard.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -o pipefail 4 | 5 | ## This script is used to deploy spinnaker in halyard K8S pod 6 | ## 1. Install helm CLI 7 | ## 2. Configure default K8S context inside halyard pod 8 | ## 3. Install Minio Server as service in k8s cluster and configure as storage service for spinnaker 9 | ## 4. Configure kubernetes account with spinnaker 10 | ## 5. deploy spinnaker using halyard command 11 | 12 | 13 | function installHelmCLI() { 14 | cd /home/spinnaker/ 15 | curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 16 | sed -i 's/\/usr\/local\/bin/\/home\/spinnaker/g' get_helm.sh 17 | sed -i 's/sudo //g' get_helm.sh 18 | export PATH=/home/spinnaker:$PATH 19 | chmod 700 get_helm.sh 20 | ./get_helm.sh 21 | } 22 | function configureDefaultK8SContext() { 23 | kubectl config set-cluster default --server=https://kubernetes.default --certificate-authority=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt 24 | kubectl config set-context default --cluster=default 25 | token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) 26 | kubectl config set-credentials user --token=$token 27 | kubectl config set-context default --user=user 28 | kubectl config use-context default 29 | } 30 | 31 | function installMinioService() { 32 | helm repo add minio https://helm.min.io/ 33 | helm install my-release minio/minio || true 34 | 35 | ACCESS_KEY=$(kubectl get secret -n default my-release-minio -o jsonpath="{.data.accesskey}" | base64 -d) 36 | SECRET_KEY=$(kubectl get secret -n default my-release-minio -o jsonpath="{.data.secretkey}" | base64 -d) 37 | MINIO_END_POINT=$(kubectl get endpoints my-release-minio -n default | grep my-release-minio | awk '{print $2}') 38 | 39 | count=0 40 | max_count=10 41 | echo "Check minio endpoint is configured." 42 | while [ $count -lt $max_count ]; do 43 | echo "MINIO_END_POINT ==> $MINIO_END_POINT" 44 | if [[ $MINIO_END_POINT != "" ]]; then 45 | break 46 | fi 47 | count=$[$count +1] 48 | echo "Sleep for 5Sec before next attempt." 49 | sleep 5 50 | MINIO_END_POINT=$(kubectl get endpoints my-release-minio -n default | grep my-release-minio | awk '{print $2}') 51 | done 52 | if [[ $MINIO_END_POINT == "" ]]; then 53 | echo "Unable to get the minio endpoint - $MINIO_END_POINT" 54 | exit 1 55 | fi 56 | 57 | echo $SECRET_KEY | \ 58 | hal config storage s3 edit --endpoint http://$MINIO_END_POINT \ 59 | --access-key-id $ACCESS_KEY \ 60 | --secret-access-key 61 | 62 | hal config storage edit --type s3 63 | 64 | mkdir -p ~/.hal/default/profiles && touch ~/.hal/default/profiles/front50-local.yml 65 | echo 'spinnaker.s3.versioning: false' > ~/.hal/default/profiles/front50-local.yml 66 | } 67 | 68 | function configureK8SAccountWithSpinnaker() { 69 | hal config provider kubernetes enable 70 | K8S_ACCOUNT=poc-k8s-account 71 | hal config provider kubernetes account add $K8S_ACCOUNT \ 72 | --provider-version v2 \ 73 | --context $(kubectl config current-context) || true 74 | hal config deploy edit --type distributed --account-name $K8S_ACCOUNT 75 | } 76 | 77 | function deploySpinnaker() { 78 | hal config version edit --version 1.26.6 79 | hal deploy apply 80 | echo "Sleep for 20Sec to initialize spinnaker micro services" 81 | sleep 20 82 | } 83 | 84 | ######## 85 | ## Main 86 | ####### 87 | installHelmCLI 88 | echo "Checking if required CLIs are installed inside hal pod" 89 | helm version > /dev/null 90 | hal -v > /dev/null 91 | configureDefaultK8SContext 92 | installMinioService 93 | configureK8SAccountWithSpinnaker 94 | deploySpinnaker 95 | exit 0 96 | -------------------------------------------------------------------------------- /poc/spinnaker/installAndConfigSpinnaker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -o pipefail 4 | 5 | ## This script is used to install configure spinnaker to run with the sig-events poc 6 | ## 1. Create hal stable deployment and update with Spinnaker service account 7 | ## 2. Deploy distributed spinnaker in K8S cluster using halyard 8 | ## 3. Update spin-echo with the cdevents API and build new image 9 | ## 3. Create a trigger to subscribe spinnaker event API 10 | ## 4. Launch spinnaker deployment and create Application/Pipeline using spin CLI 11 | ## 5. Create Ingress to access spinnaker UI from host 12 | 13 | 14 | BASE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 15 | 16 | ## Declare variables 17 | GIT_PATH_SIGEVENTS=$BASE_DIR/../../ 18 | SPIN_ECHO_GIT_URL="git@github.com:rjalander/echo.git -b cdevent_consume" 19 | 20 | function deployHalAndUpdateServiceAccount() { 21 | kubectl delete deployment hal || true 22 | kubectl create deployment hal --image gcr.io/spinnaker-marketplace/halyard:stable 23 | cd $GIT_PATH_SIGEVENTS/poc/spinnaker 24 | kubectl create -f spinnakerServiceAccount.yaml 25 | kubectl patch deployment hal --patch '{"spec": {"template": {"spec": {"serviceAccountName":"spinnaker-service-account"}}}}' 26 | echo "Sleep for 10Sec for hal deployment to start" 27 | sleep 10 28 | } 29 | 30 | function checkHalPodIsRunning() { 31 | HAL_POD_NAME=$(kubectl get pods -l app=hal --field-selector=status.phase==Running -o jsonpath="{.items[0].metadata.name}") 32 | count=0 33 | max_count=20 34 | echo "Check hal pod is running." 35 | while [ $count -lt $max_count ]; do 36 | echo "HAL_POD_NAME ==> $HAL_POD_NAME" 37 | if [[ $HAL_POD_NAME != "" ]]; then 38 | break 39 | fi 40 | count=$[$count +1] 41 | echo "Sleep for 5Sec before next attempt." 42 | sleep 5 43 | HAL_POD_NAME=$(kubectl get pods -l app=hal --field-selector=status.phase==Running -o jsonpath="{.items[0].metadata.name}") 44 | done 45 | if [[ $HAL_POD_NAME == "" ]]; then 46 | echo "Hal pod is not running, exiting." 47 | exit 1 48 | fi 49 | } 50 | 51 | function deploySpinnakerWithHalPod() { 52 | checkHalPodIsRunning 53 | HAL_POD_NAME=$(kubectl get pods -l app=hal --field-selector=status.phase==Running -o jsonpath="{.items[0].metadata.name}") 54 | chmod +x $GIT_PATH_SIGEVENTS/poc/spinnaker/deploySpinnakerWithHalyard.sh 55 | kubectl cp $GIT_PATH_SIGEVENTS/poc/spinnaker/deploySpinnakerWithHalyard.sh default/$HAL_POD_NAME:/home/spinnaker/ 56 | kubectl exec -it $HAL_POD_NAME -- /bin/bash -c "/home/spinnaker/deploySpinnakerWithHalyard.sh" 57 | } 58 | 59 | function updateSpinEchoWithCDEventsAPI() { 60 | rm -rf ~/dev/spinnaker/echo 61 | mkdir -p ~/dev/spinnaker/echo 62 | git clone $SPIN_ECHO_GIT_URL ~/dev/spinnaker/echo 63 | cd ~/dev/spinnaker/echo 64 | rm -rf ./echo.yml ./spinnaker.yml 65 | checkHalPodIsRunning 66 | HAL_POD_NAME=$(kubectl get pods -l app=hal --field-selector=status.phase==Running -o jsonpath="{.items[0].metadata.name}") 67 | kubectl cp default/$HAL_POD_NAME:/home/spinnaker/.hal/default/staging/spinnaker.yml ./spinnaker.yml 68 | kubectl cp default/$HAL_POD_NAME:/home/spinnaker/.hal/default/staging/echo.yml ./echo.yml 69 | ./gradlew echo-web:installDist -x test && docker build -f Dockerfile.slim --tag localhost:5000/cdevents/spinnaker-echo-poc . 70 | docker push localhost:5000/cdevents/spinnaker-echo-poc:latest 71 | 72 | cd $GIT_PATH_SIGEVENTS/poc/spinnaker 73 | kubectl delete -f spin-echo-deploy.yaml || true 74 | kubectl apply -f spin-echo-deploy.yaml 75 | echo "Sleep for 20Sec to initialize spinnaker poc echo service" 76 | sleep 20 77 | } 78 | 79 | function createTriggerToSubscribeSpinnakerEvent() { 80 | kubectl create -f - <> /dev/null 148 | 149 | deployHalAndUpdateServiceAccount 150 | deploySpinnakerWithHalPod 151 | updateSpinEchoWithCDEventsAPI 152 | createTriggerToSubscribeSpinnakerEvent 153 | createApplicationAndPipeline 154 | createIngressSpinnakerUI 155 | exit 0 156 | -------------------------------------------------------------------------------- /poc/spinnaker/spin-echo-deploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: spin-echo-poc-deployment 5 | namespace: spinnaker 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: spinnaker-echo-poc 11 | template: 12 | metadata: 13 | labels: 14 | app: spinnaker-echo-poc 15 | spec: 16 | containers: 17 | - image: 'localhost:5000/cdevents/spinnaker-echo-poc' 18 | name: spinnaker-echo-poc 19 | ports: 20 | - containerPort: 8089 21 | --- 22 | apiVersion: v1 23 | kind: Service 24 | metadata: 25 | name: spin-echo 26 | namespace: spinnaker 27 | spec: 28 | ports: 29 | - port: 8089 30 | targetPort: 8089 31 | selector: 32 | app: spinnaker-echo-poc 33 | type: ClusterIP 34 | -------------------------------------------------------------------------------- /poc/spinnaker/spinnakerServiceAccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: spinnaker-service-account 5 | namespace: default 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1 8 | kind: ClusterRoleBinding 9 | metadata: 10 | name: spinnaker-role-binding 11 | roleRef: 12 | apiGroup: rbac.authorization.k8s.io 13 | kind: ClusterRole 14 | name: cluster-admin 15 | subjects: 16 | - namespace: default 17 | kind: ServiceAccount 18 | name: spinnaker-service-account -------------------------------------------------------------------------------- /poc/tekton/README.md: -------------------------------------------------------------------------------- 1 | # Tekton Resources 2 | 3 | This folder contains all the Tekton resources required for the PoC. 4 | 5 | ## Prerequisites 6 | 7 | ### Install Tekton 8 | 9 | Set up a kind cluster with Tekton using [tekton_in_kind.sh](../tekton_in_kind.sh). 10 | Alternatively: 11 | 12 | 1. Install Tekton Pipeline: 13 | ```bash 14 | kubectl apply -f https://storage.googleapis.com/tekton-releases/pipeline/previous/v0.25.0/release.yaml 15 | ``` 16 | 17 | 2. Install Tekton Triggers: 18 | ```bash 19 | kubectl apply -f https://storage.googleapis.com/tekton-releases/triggers/previous/v0.14.2/release.yaml 20 | kubectl apply -f https://storage.googleapis.com/tekton-releases/triggers/previous/v0.14.2/interceptors.yaml 21 | ``` 22 | 23 | 3. Install Tekton Dashboard (optional): 24 | ```bash 25 | kubectl apply -f https://github.com/tektoncd/dashboard/releases/download/v0.17.0/tekton-dashboard-release.yaml 26 | ``` 27 | 28 | ### Configure Tekton Pipeline 29 | 30 | The Tekton pipeline uses some alpha features (Tekton Bundles) which needs to 31 | be enabled in the config. The `feature-flags` config map in the `tekton-pipelines` namespace 32 | should look like: 33 | 34 | ```yaml 35 | apiVersion: v1 36 | data: 37 | enable-api-fields: alpha # <------- 38 | disable-affinity-assistant: "false" 39 | disable-creds-init: "false" 40 | disable-home-env-overwrite: "true" 41 | disable-working-directory-overwrite: "true" 42 | enable-custom-tasks: "false" 43 | enable-tekton-oci-bundles: "false" 44 | require-git-ssh-secret-known-hosts: "false" 45 | running-in-environment-with-injected-sidecars: "true" 46 | kind: ConfigMap 47 | (...) 48 | ``` 49 | 50 | ### Install Tekton CloudEvents Controller 51 | 52 | Nightly builds are not available yet, so this has to be installed from source. 53 | Requires: 54 | 55 | - go 1.16+ 56 | - `GO111MODULE=on` 57 | - [`ko`](https://github.com/google/ko) 58 | 59 | Clone the [tekton experimental repo](https://github.com/tektoncd/experimental). 60 | 61 | Setup `KO_DOCKER_REPO` to point to your container registry (or `kind.local` when using kind). 62 | Note than when using a private container registry, the tekton cloudevents controller service account 63 | will need to have access via `imagePullSecrets`. 64 | 65 | Move to the cloned folder: 66 | ``` 67 | cd cloudevents 68 | ko apply -f config/ 69 | ``` 70 | 71 | ### Configure Tekton CloudEvents Controller 72 | 73 | The `config-defaults` config map in the `tekton-cloudevents` namespace should look like: 74 | 75 | ```yaml 76 | apiVersion: v1 77 | kind: ConfigMap 78 | data: 79 | default-cloud-events-sink: http:// 80 | (...) 81 | ``` 82 | 83 | ## Install Tekton Resources 84 | 85 | Install all the yaml files: 86 | 87 | ```bash 88 | kubectl create -f poc/tekton 89 | ``` 90 | 91 | ## Run the build pipeline 92 | 93 | Prerequisites: 94 | - `tkn` (github.com/tektoncd/cli) 95 | - Push access to a container registry. The `dockerconfig` secret in the example below is called `regcred` 96 | 97 | ```bash 98 | tkn pipeline start build-artifact -w name=sources,volumeClaimTemplateFile=poc/tekton/workspace-template.yaml -w name=dockerconfig,secret=regcred 99 | ``` 100 | -------------------------------------------------------------------------------- /poc/tekton/rbac.yaml: -------------------------------------------------------------------------------- 1 | kind: Role 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | metadata: 4 | name: triggers-minimal 5 | rules: 6 | - apiGroups: [""] 7 | resources: ["configmaps", "secrets"] 8 | verbs: ["get", "list", "watch"] 9 | - apiGroups: ["triggers.tekton.dev"] 10 | resources: ["eventlisteners", "triggerbindings", "triggertemplates", "triggers"] 11 | verbs: ["get", "list", "watch"] 12 | - apiGroups: ["tekton.dev"] 13 | resources: ["pipelineruns", "pipelineresources", "taskruns"] 14 | verbs: ["create"] 15 | - apiGroups: [""] 16 | resources: ["serviceaccounts"] 17 | verbs: ["impersonate"] 18 | --- 19 | kind: ClusterRole 20 | apiVersion: rbac.authorization.k8s.io/v1 21 | metadata: 22 | name: triggers-minimal-ci 23 | rules: 24 | # EventListeners need to be able to fetch any clustertriggerbindings 25 | - apiGroups: ["triggers.tekton.dev"] 26 | resources: ["clustertriggerbindings"] 27 | verbs: ["get", "list", "watch"] 28 | --- 29 | apiVersion: v1 30 | kind: ServiceAccount 31 | metadata: 32 | name: build-bot 33 | --- 34 | apiVersion: v1 35 | kind: ServiceAccount 36 | metadata: 37 | name: tekton-ci-workspace-listener 38 | secrets: 39 | - name: ci-webhook 40 | --- 41 | apiVersion: v1 42 | kind: ServiceAccount 43 | metadata: 44 | name: tekton-ci-jobs 45 | secrets: 46 | - name: ci-webhook 47 | --- 48 | apiVersion: rbac.authorization.k8s.io/v1 49 | kind: RoleBinding 50 | metadata: 51 | name: tekton-ci-workspace-listener-triggers-minimal 52 | subjects: 53 | - kind: ServiceAccount 54 | name: tekton-ci-workspace-listener 55 | roleRef: 56 | apiGroup: rbac.authorization.k8s.io 57 | kind: Role 58 | name: triggers-minimal 59 | --- 60 | apiVersion: rbac.authorization.k8s.io/v1 61 | kind: RoleBinding 62 | metadata: 63 | name: tekton-ci-jobs-triggers-minimal 64 | subjects: 65 | - kind: ServiceAccount 66 | name: tekton-ci-jobs 67 | roleRef: 68 | apiGroup: rbac.authorization.k8s.io 69 | kind: Role 70 | name: triggers-minimal 71 | --- 72 | apiVersion: rbac.authorization.k8s.io/v1 73 | kind: ClusterRoleBinding 74 | metadata: 75 | name: tekton-ci-workspace-listener-triggers-minimal-ci 76 | subjects: 77 | - kind: ServiceAccount 78 | name: tekton-ci-workspace-listener 79 | namespace: default 80 | roleRef: 81 | apiGroup: rbac.authorization.k8s.io 82 | kind: ClusterRole 83 | name: triggers-minimal 84 | --- 85 | kind: Role 86 | apiVersion: rbac.authorization.k8s.io/v1 87 | metadata: 88 | name: config-map-admin 89 | rules: 90 | - apiGroups: [""] 91 | resources: ["configmaps"] 92 | verbs: ["*"] 93 | --- 94 | apiVersion: rbac.authorization.k8s.io/v1 95 | kind: RoleBinding 96 | metadata: 97 | name: default-config-map-admin 98 | subjects: 99 | - kind: ServiceAccount 100 | name: default 101 | roleRef: 102 | apiGroup: rbac.authorization.k8s.io 103 | kind: Role 104 | name: config-map-admin 105 | --- 106 | kind: ClusterRole 107 | apiVersion: rbac.authorization.k8s.io/v1 108 | metadata: 109 | name: tekton-ci-jobs-triggers 110 | rules: 111 | - apiGroups: ["triggers.tekton.dev"] 112 | resources: ["eventlisteners", "triggerbindings", "triggertemplates", "triggers", "clustertriggerbindings"] 113 | verbs: ["get", "list", "watch"] 114 | --- 115 | apiVersion: rbac.authorization.k8s.io/v1 116 | kind: ClusterRoleBinding 117 | metadata: 118 | name: tekton-ci-jobs-triggers-tekton-ci-workspace-listener 119 | subjects: 120 | - kind: ServiceAccount 121 | name: tekton-ci-workspace-listener 122 | namespace: default 123 | roleRef: 124 | apiGroup: rbac.authorization.k8s.io 125 | kind: ClusterRole 126 | name: tekton-ci-jobs-triggers 127 | --- 128 | kind: Role 129 | apiVersion: rbac.authorization.k8s.io/v1 130 | metadata: 131 | name: deployer2default 132 | rules: 133 | - apiGroups: [""] 134 | resources: ["pods", "services"] 135 | verbs: ["*"] 136 | - apiGroups: ["apps"] 137 | resources: ["deployments", "replicasets", "deployments/scale"] 138 | verbs: ["*"] 139 | - apiGroups: [""] 140 | resources: ["configmaps", "secrets"] 141 | verbs: ["get", "list", "watch"] 142 | - apiGroups: ["networking.k8s.io"] 143 | resources: ["ingresses"] 144 | verbs: ["*"] 145 | --- 146 | apiVersion: rbac.authorization.k8s.io/v1 147 | kind: RoleBinding 148 | metadata: 149 | name: default-deployer2default 150 | subjects: 151 | - kind: ServiceAccount 152 | name: default 153 | roleRef: 154 | apiGroup: rbac.authorization.k8s.io 155 | kind: Role 156 | name: deployer2default -------------------------------------------------------------------------------- /poc/tekton/resources/0000_namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: cdevents 5 | -------------------------------------------------------------------------------- /poc/tekton/resources/0001_serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | kind: Role 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | metadata: 4 | name: triggers-minimal 5 | namespace: cdevents 6 | rules: 7 | - apiGroups: [""] 8 | resources: ["configmaps", "secrets"] 9 | verbs: ["get", "list", "watch"] 10 | - apiGroups: ["triggers.tekton.dev"] 11 | resources: ["eventlisteners", "triggerbindings", "triggertemplates", "triggers"] 12 | verbs: ["get", "list", "watch"] 13 | - apiGroups: ["tekton.dev"] 14 | resources: ["pipelineruns", "pipelineresources", "taskruns"] 15 | verbs: ["create"] 16 | - apiGroups: [""] 17 | resources: ["serviceaccounts"] 18 | verbs: ["impersonate"] 19 | --- 20 | apiVersion: v1 21 | kind: ServiceAccount 22 | metadata: 23 | name: cdevent-listener 24 | namespace: cdevents 25 | --- 26 | apiVersion: rbac.authorization.k8s.io/v1 27 | kind: RoleBinding 28 | metadata: 29 | name: cdevent-listener-triggers-minimal 30 | namespace: cdevents 31 | subjects: 32 | - kind: ServiceAccount 33 | name: cdevent-listener 34 | roleRef: 35 | apiGroup: rbac.authorization.k8s.io 36 | kind: Role 37 | name: triggers-minimal 38 | --- 39 | kind: Role 40 | apiVersion: rbac.authorization.k8s.io/v1 41 | metadata: 42 | name: config-map-admin 43 | namespace: cdevents 44 | rules: 45 | - apiGroups: [""] 46 | resources: ["configmaps"] 47 | verbs: ["*"] 48 | --- 49 | apiVersion: rbac.authorization.k8s.io/v1 50 | kind: RoleBinding 51 | metadata: 52 | name: default-config-map-admin 53 | namespace: cdevents 54 | subjects: 55 | - kind: ServiceAccount 56 | name: default 57 | roleRef: 58 | apiGroup: rbac.authorization.k8s.io 59 | kind: Role 60 | name: config-map-admin 61 | --- 62 | kind: ClusterRole 63 | apiVersion: rbac.authorization.k8s.io/v1 64 | metadata: 65 | name: cdevent-listener-triggers 66 | rules: 67 | - apiGroups: ["triggers.tekton.dev"] 68 | resources: ["eventlisteners", "triggerbindings", "triggertemplates", "triggers", "clustertriggerbindings", "clusterinterceptors"] 69 | verbs: ["get", "list", "watch"] 70 | --- 71 | apiVersion: rbac.authorization.k8s.io/v1 72 | kind: ClusterRoleBinding 73 | metadata: 74 | name: cdevent-listener-triggers-cdevents 75 | subjects: 76 | - kind: ServiceAccount 77 | name: cdevent-listener 78 | namespace: cdevents 79 | roleRef: 80 | apiGroup: rbac.authorization.k8s.io 81 | kind: ClusterRole 82 | name: cdevent-listener-triggers 83 | --- 84 | apiVersion: v1 85 | kind: ServiceAccount 86 | metadata: 87 | name: runner 88 | namespace: cdevents 89 | --- 90 | apiVersion: rbac.authorization.k8s.io/v1 91 | kind: ClusterRole 92 | metadata: 93 | name: deployer 94 | rules: 95 | - apiGroups: [""] 96 | resources: [services] 97 | verbs: ["*"] 98 | - apiGroups: [""] 99 | resources: [pods] 100 | verbs: ["get", "list", "watch"] 101 | - apiGroups: [apps] 102 | resources: [deployments] 103 | verbs: ["*"] 104 | - apiGroups: [networking.k8s.io] 105 | resources: [ingresses] 106 | verbs: ["*"] 107 | --- 108 | apiVersion: rbac.authorization.k8s.io/v1 109 | kind: RoleBinding 110 | metadata: 111 | name: runner-deployer-production 112 | namespace: production 113 | subjects: 114 | - kind: ServiceAccount 115 | name: runner 116 | namespace: cdevents 117 | roleRef: 118 | apiGroup: rbac.authorization.k8s.io 119 | kind: ClusterRole 120 | name: deployer -------------------------------------------------------------------------------- /poc/tekton/resources/0002_eventlistener.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: triggers.tekton.dev/v1alpha1 3 | kind: EventListener 4 | metadata: 5 | name: cdevent-listener 6 | namespace: cdevents 7 | spec: 8 | serviceAccountName: cdevent-listener 9 | namespaceSelector: 10 | matchNames: ['cdevents'] -------------------------------------------------------------------------------- /poc/tekton/resources/0003_triggers.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: triggers.tekton.dev/v1alpha1 3 | kind: Trigger 4 | metadata: 5 | name: cdevent-artifact-published 6 | namespace: cdevents 7 | spec: 8 | interceptors: 9 | - ref: 10 | name: "cel" 11 | params: 12 | - name: "filter" 13 | value: "header.match('Ce-Type', 'cd.artifact.published.v1')" 14 | - name: "overlays" 15 | value: 16 | - key: artifactId 17 | expression: "header['Ce-Artifactid'][0].replace('kind-registry', 'localhost')" 18 | bindings: 19 | - name: artifactId 20 | value: $(extensions.artifactId) 21 | - name: artifactName 22 | value: $(header.Ce-Artifactname) 23 | - name: shkeptncontext 24 | value: $(body.shkeptncontext) 25 | - name: triggerid 26 | value: $(body.triggerid) 27 | template: 28 | spec: 29 | params: 30 | - name: artifactId 31 | - name: artifactName 32 | - name: shkeptncontext 33 | - name: triggerid 34 | resourceTemplates: 35 | - apiVersion: "tekton.dev/v1beta1" 36 | kind: PipelineRun 37 | metadata: 38 | generateName: "deploy-artifact-" 39 | spec: 40 | serviceAccountName: runner 41 | pipelineRef: 42 | name: deploy-artifact 43 | params: 44 | - name: image 45 | value: $(tt.params.artifactId) 46 | - name: name 47 | value: $(tt.params.artifactName) 48 | - name: shkeptncontext 49 | value: $(tt.params.shkeptncontext) 50 | - name: triggerid 51 | value: $(tt.params.triggerid) 52 | - name: envId 53 | value: "production" 54 | -------------------------------------------------------------------------------- /poc/tekton/resources/0004_pipelines.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Pipeline 3 | metadata: 4 | name: build-artifact 5 | annotations: 6 | description: | 7 | Build the container image of the Podtato Head service 8 | cd.artifact.packaged: enabled 9 | spec: 10 | params: 11 | - name: gitRepository 12 | description: The git repository that hosts context and Dockerfile 13 | default: https://github.com/cdfoundation/sig-events 14 | - name: gitRevision 15 | description: The git revision to build 16 | default: main 17 | - name: context 18 | description: The path to the docker context in the sources workspace 19 | default: poc/docker 20 | - name: dockerfile 21 | description: The path to the dockerfile within the context 22 | default: Dockerfile 23 | - name: target 24 | description: The target container registry and path where to build the image 25 | default: kind-registry:5000/cdevent 26 | - name: target-name 27 | description: Name of the image to publish 28 | default: poc 29 | - name: version 30 | description: the target version to build 31 | default: v1.0 32 | - name: kanikoExtraArgs 33 | type: array 34 | description: extra args to pass to the kaniko builder 35 | default: [--insecure-registry=kind-registry:5000] 36 | workspaces: 37 | - name: sources 38 | description: Workspace where the git repo is prepared for testing 39 | - name: dockerconfig 40 | description: Docker config secret 41 | optional: true 42 | results: 43 | - name: cd.artifact.id 44 | value: $(tasks.export-results.results.artifactId) 45 | - name: cd.artifact.name 46 | value: $(tasks.export-results.results.artifactName) 47 | - name: cd.artifact.version 48 | value: $(tasks.export-results.results.artifactVersion) 49 | tasks: 50 | - name: clone-repo 51 | taskRef: 52 | name: git-clone 53 | bundle: gcr.io/tekton-releases/catalog/upstream/git-clone:0.4 54 | params: 55 | - name: url 56 | value: $(params.gitRepository) 57 | - name: revision 58 | value: $(params.gitRevision) 59 | workspaces: 60 | - name: output 61 | workspace: sources 62 | - name: image-build 63 | runAfter: [clone-repo] 64 | taskRef: 65 | name: kaniko 66 | bundle: gcr.io/tekton-releases/catalog/upstream/kaniko:0.4 67 | params: 68 | - name: IMAGE 69 | value: $(params.target)/$(params.target-name):$(params.version) 70 | - name: CONTEXT 71 | value: $(params.context) 72 | - name: DOCKERFILE 73 | value: $(params.dockerfile) 74 | - name: EXTRA_ARGS 75 | value: ["$(params.kanikoExtraArgs[*])"] 76 | workspaces: 77 | - name: source 78 | workspace: sources 79 | - name: dockerconfig 80 | workspace: dockerconfig 81 | - name: export-results 82 | runAfter: [image-build] 83 | taskSpec: 84 | params: 85 | - name: name 86 | - name: version 87 | - name: uri 88 | results: 89 | - name: artifactName 90 | description: The name of the artifact 91 | - name: artifactVersion 92 | description: The version of the artifact 93 | - name: artifactId 94 | description: The full URL of the artifact, with version 95 | steps: 96 | - name: create-results 97 | image: alpine 98 | env: 99 | - name: ARTIFACT_NAME_RESULT_PATH 100 | value: $(results.artifactName.path) 101 | - name: ARTIFACT_VERSION_RESULT_PATH 102 | value: $(results.artifactVersion.path) 103 | - name: ARTIFACT_ID_RESULT_PATH 104 | value: $(results.artifactId.path) 105 | - name: ARTIFACT_NAME_RESULT_VALUE 106 | value: $(params.name) 107 | - name: ARTIFACT_VERSION_RESULT_VALUE 108 | value: $(params.version) 109 | - name: ARTIFACT_ID_RESULT_VALUE 110 | value: $(params.uri) 111 | script: | 112 | printf "$ARTIFACT_NAME_RESULT_VALUE" > "$ARTIFACT_NAME_RESULT_PATH" 113 | printf "$ARTIFACT_VERSION_RESULT_VALUE" > "$ARTIFACT_VERSION_RESULT_PATH" 114 | printf "$ARTIFACT_ID_RESULT_VALUE" > "$ARTIFACT_ID_RESULT_PATH" 115 | params: 116 | - name: name 117 | value: $(params.target-name) 118 | - name: version 119 | value: $(params.version) 120 | - name: uri 121 | value: $(params.target)/$(params.target-name)@$(tasks.image-build.results.IMAGE-DIGEST) 122 | --- 123 | apiVersion: tekton.dev/v1beta1 124 | kind: Pipeline 125 | metadata: 126 | name: deploy-artifact 127 | namespace: cdevents 128 | annotations: 129 | description: | 130 | Deploy the Podtato Head service 131 | cd.service.deployed: enabled 132 | spec: 133 | params: 134 | - name: image 135 | - name: name 136 | - name: shkeptncontext 137 | - name: triggerid 138 | - name: envId 139 | results: 140 | - name: cd.service.envId 141 | value: $(tasks.deploy.results.envId) 142 | - name: cd.service.name 143 | value: $(tasks.deploy.results.serviceName) 144 | - name: cd.service.version 145 | value: $(tasks.deploy.results.serviceVersion) 146 | - name: sh.keptn.context 147 | value: $(tasks.deploy.results.shkeptncontext) 148 | - name: sh.keptn.trigger.id 149 | value: $(tasks.deploy.results.shkeptntriggerid) 150 | tasks: 151 | - name: deploy 152 | taskSpec: 153 | params: 154 | - name: envId 155 | - name: name 156 | - name: image 157 | - name: shkeptncontext 158 | - name: shkeptntriggerid 159 | results: 160 | - name: envId 161 | - name: serviceName 162 | - name: serviceVersion 163 | - name: shkeptncontext 164 | - name: shkeptntriggerid 165 | stepTemplate: 166 | env: 167 | - name: NAMESPACE 168 | value: $(params.envId) 169 | - name: SERVICE_NAME 170 | value: $(params.name) 171 | - name: CONTAINER_IMAGE 172 | value: $(params.image) 173 | steps: 174 | - name: deploy 175 | image: docker.io/bitnami/kubectl@sha256:792e0aaabee6c51d734b5d3b150ba990d6106c4c081731d8570c1b9afe0c52d9 176 | script: | 177 | #!/bin/sh 178 | set -ex 179 | 180 | # Try to create a deployment, in case it's our first run 181 | kubectl create deployment ${SERVICE_NAME} --image ${CONTAINER_IMAGE} -n ${NAMESPACE} || true 182 | 183 | # Update the deployment, rollout new image 184 | kubectl set image deployment/${SERVICE_NAME} *=${CONTAINER_IMAGE} -n ${NAMESPACE} 185 | 186 | # Create a service if it doesn't exists yet 187 | kubectl expose deployment/${SERVICE_NAME} --port 8080 --target-port 80 -n ${NAMESPACE} || true 188 | 189 | - name: ingress 190 | image: docker.io/bitnami/kubectl@sha256:792e0aaabee6c51d734b5d3b150ba990d6106c4c081731d8570c1b9afe0c52d9 191 | script: | 192 | #!/bin/bash 193 | set -ex 194 | 195 | # Create an Ingress (unless it exists already) 196 | cat < $(results.shkeptncontext.path) 219 | printf "$(params.shkeptntriggerid)" > $(results.shkeptntriggerid.path) 220 | printf "http://${SERVICE_NAME}-127.0.0.1.nip.io" > $(results.envId.path) 221 | printf "${SERVICE_NAME}" > $(results.serviceName.path) 222 | # Export the deployment generation as serviceVersion 223 | kubectl get deployment/${SERVICE_NAME} -n ${NAMESPACE} -o jsonpath='{.metadata.generation}' > $(results.serviceVersion.path) 224 | 225 | params: 226 | - name: envId 227 | value: $(params.envId) 228 | - name: name 229 | value: $(params.name) 230 | - name: image 231 | value: $(params.image) 232 | - name: shkeptncontext 233 | value: $(params.shkeptncontext) 234 | - name: shkeptntriggerid 235 | value: $(params.triggerid) 236 | -------------------------------------------------------------------------------- /poc/tekton/workspace-template.yaml: -------------------------------------------------------------------------------- 1 | spec: 2 | accessModes: 3 | - ReadWriteOnce 4 | resources: 5 | requests: 6 | storage: 300Mi 7 | -------------------------------------------------------------------------------- /vocabulary-draft/README.md: -------------------------------------------------------------------------------- 1 | ***Note! This draft vocabulary/specification is no longer maintained. Please go to the official [CDEvents website](https://cdevents.dev/) to follow the progress there.*** 2 | 3 | # Continuous Delivery Events 4 | 5 | Continuous Delivery Events (CDEvents) is a common event format for the Continuous Delivery (CD) ecosystem. 6 | 7 | CDEvents are an enabler for interoperability between different CI/CD tools as they provide a tool agnostic communication protocol. 8 | 9 | ## Events Format 10 | 11 | CDEvents are [CloudEvents](https://cloudevents.io/) with specific extensions and payload structure, which is based on the [CDEvents vocabulary](introduction.md). 12 | 13 | Events producer may use the payload to provide extra context to the events consumers; the payload however is not meant to 14 | transport large amounts of data. Data such as logs or software artifacts should be linked to from the event and not 15 | embedded into the events. We follow the [CloudEvents recommendation](https://github.com/cloudevents/spec/blob/v1.0.1/spec.md#size-limits) 16 | on events size and size limits. 17 | 18 | ### Specification 19 | 20 | The aim of the Events SIG is to produce a version specification for CDEvents. The specification was initially defined in the [draft CDE vocabulary](introduction.md). 21 | 22 | ## Tools 23 | 24 | The draft specification of the CDEvents is combined with an [SDK and CLI](https://github.com/cdfoundation/sig-events/blob/main/cde/sdk/go/README.md). 25 | 26 | ## Proof of Concept 27 | 28 | There is a [Proof of Concept](../poc/README.md) demonstrating how to use draft CDEvents specification and its SDK. 29 | -------------------------------------------------------------------------------- /vocabulary-draft/continuous-deployment-pipeline-events.md: -------------------------------------------------------------------------------- 1 | # Continuous Deployment Pipelines Events 2 | 3 | __Note:__ This is a work-in-progress draft version and is being worked on by members of the Events SIG. You are very welcome to join the work and the discussions! 4 | 5 | These events are related to continous deployment pipelines and their target environments. 6 | These events can be emitted by environments to report where software artifacts such as services, binaries, deamons, jobs or embedded software are running. 7 | 8 | The term Service is used to represent a running Artifact. This service can represent a binary that is running, a deamon, an application, a docker container, etc. 9 | The term Environment represent any platform which has all the means to run a Service. 10 | 11 | - **Environment Created**: an environment has been created and it can be used to deploy Services 12 | - **Environment Modified**: an environment has been modified, this event advertise the changes made in the environment 13 | - **Environment Deleted**: an environment has been deleted and cannot longer be used 14 | - **Service Deployed**: a new instance of the Service has been deployed 15 | - **Service Upgraded**: an existing instance of a Service has been upgraded to a new version 16 | - **Service Rolledback**: an existing instance of a Service has been rolledback to a previous version 17 | - **Service Removed**: an existing instance of a Service has been terminated an it is not longer present in an environment 18 | 19 | Continuous Deployment Events MUST include the following attributes: 20 | - **Event Type**: the type is restricted to include `cd.**` prefix. For example `cd.service.upgraded` or `cd.environment.created` 21 | - **Environment ID**: unique identifier for the Environment 22 | 23 | 24 | Optional attributes: 25 | 26 | - **Environment Name**: user-friendly name for the environment, to be displayed in tools or User Interfaces 27 | - **Environment URL**: URL to reference where the environment is located 28 | -------------------------------------------------------------------------------- /vocabulary-draft/continuous-integration-pipeline-events.md: -------------------------------------------------------------------------------- 1 | # Continuous Integration Events 2 | 3 | __Note:__ This is a work-in-progress draft version and is being worked on by members of the Events SIG. You are very welcome to join the work and the discussions! 4 | 5 | These events are related to **Continuous Integration(CI)** activities. CI usually include activities such as building, testing, packaging and releasing software artifacts. 6 | 7 | The following events represent concrete Tasks that are associated with the execution of CI pipelines: 8 | 9 | - **Build Queued**: a Build task has been queued, this build process usually is in charge of producing a binary from source code 10 | - **Build Started**: a Build task has started 11 | - **Build Finished**: a Build task has finished, the event will contain the finished status, success, error or failure 12 | 13 | The following Test events are defined in two separate categories **Test Case** and **Test Suite**. A **Test Case** is the smallest unit of testing that the user wants to track. A **Test Suite** is a collection of test case executions and/or other test suite executions. **Test Cases** executed, and Test Suites are for grouping purposes. For this reason, **Test Cases** can be queued. 14 | 15 | - **Test Case Queued**: a Test task has been queued, and it is waiting to be started 16 | - **Test Case Started**: a Test task has started 17 | - **Test Case Finished**: a Test task has finished, the event will contain the finished status: success, error or failure 18 | - **Test Suite Started**: a Test Suite has started 19 | - **Test Suite Finished**: a Test Suite has finished, the event will contain the finished status: success, error or failure 20 | 21 | Finally, events needs to be generated for the output of the pipeline such as the artifacts that were packaged and released for others to use. 22 | 23 | - **Artifact Packaged**: an artifact has been packaged for distribution, this artifact is now versioned with a fixed version 24 | - **Artifact Published**: an artifact has been published and it can be advertised for others to use 25 | 26 | CI Events MUST include the following attributes: 27 | - **Event Type**: the type is restricted to include `cd.**` prefix. For example `cd.build.queued` or `cd.artifact.packaged` 28 | 29 | Optional attributes: 30 | - **Artifact Id**: the unique identifier of the artifact that the event is refering to. 31 | -------------------------------------------------------------------------------- /vocabulary-draft/core.md: -------------------------------------------------------------------------------- 1 | # Continuous Delivery Core Events 2 | 3 | __Note:__ This is a work-in-progress draft version and is being worked on by members of the Events SIG. You are very welcome to join the work and the discussions! 4 | 5 | Continuous Delivery related to activities and orchestration that needs to exist to be able to deterministically and continously being able to delivery software to users. 6 | 7 | A pipeline, in the context of Continuous Delivery, is the definition of a set of tasks that needs to be performed to build, test, package, release and deploy software artifacts. A pipeline can be instanciated multiple times, for example to build different versions of the same artifact. We are refering to this instance as PipelineRun. It will have a unique Id and it will help us to track the build and release progress on a particular software artifact. 8 | 9 | Due the dynamic nature of Pipelines, most of actual work needs to be queued to happen in a distributed way, hence Queued events are added. Adopters can choose to ignore these events if they don't apply to their use cases. 10 | 11 | - **PipelineRun Queued**: a PipelineRun has been schedule to run 12 | - **PipelineRun Started**: a PipelineRun has started and it is running 13 | - **PipelineRun Finished**: a PipelineRun has finished it execution, the event will contain the finished status, success, error or failure 14 | 15 | Each pipeline is defined as a set of Tasks to be performed in sequence, hence tracking this sequence might be important for some cases. A TaskRun is an instance of the Task defined inside the pipeline, as you can expect multiple execution of the pipelines (each a PipelineRun) you can also expect multiple execution of the Tasks, for that reason we use TaskRun to refer to one of these instances. 16 | 17 | - **TaskRun Started**: a TaskRun inside a PipelineRun has started. 18 | - **TaskRun Finished**: a TaskRun inside a PipelineRun has finished. 19 | 20 | Pipeline Events MUST include the following attributes: 21 | - **Event Type**: the type is restricted to include `cd.**` prefix. For example `cd.pipelinerun.queued` or `cd.tests.started` 22 | - **PipelineRun Id**: unique identifier for a pipeline execution 23 | - **Pipeline Name**: unique identifier for the pipeline, not for the instance. A pipeline can have multiple instances/runs. 24 | - **PipelineRun Status**: current status of the PipelineRun at the time when the event was emitted. If the pipeline is finished, this attribute should reflect if it finished successfully or if there was an error on the execution. Possible statuses: [Running, Finished, Error] 25 | 26 | Optional attributes: 27 | - **PipelineRun URL**: URL to locate where pipeline instances are running 28 | - **PipelineRun Errors**: error field to indicate possible compilation, test, build and package errors. 29 | -------------------------------------------------------------------------------- /vocabulary-draft/introduction.md: -------------------------------------------------------------------------------- 1 | # Continuous Delivery Events Vocabulary 2 | 3 | __Note:__ This is a work-in-progress draft version and is being worked on by members of the Events SIG. You are very welcome to join the work and the discussions! 4 | 5 | This document intends to describe a set of events related to different phases of a Continuous Delivery process. 6 | These events are categorized by meaning, and the phase where they are intended to be used. 7 | These events are agnostic from any specific tool and are designed to fit a wide range of scenarios. 8 | 9 | The phases covered by this proposal are: 10 | 11 | - **[Core Events](core.md)**: includes core events related to core activities and orchestration that needs to exist to be able to deterministically and continously being able to delivery software to users. 12 | - **[Source Code Version Control Events](source-code-version-control.md)**: Events emitted by changes in source code or by the creation, modification or deletion of new repositories that hold source code. 13 | - **[Continuous Integration Pipelines Events](continuous-integration-pipeline-events.md)**: includes events related to building, testings, packaging and releasing software artifacts, usually binaries. 14 | - **[Continuous Deployment Pipelines Events](continuous-deployment-pipeline-events.md)**: include events related with environments where the artifacts produced by the integration pipelines actually run. These are usually services running in a specific environment (dev, QA, production), or embedded software running in a specific hardware. 15 | 16 | 17 | These phases can also be considered as different profiles of the vocabulary that can be adopted independently. 18 | Also notice that we currently use the term 'pipeline' to denote a pipelines, workflows and related concepts. We also use the term 'task' to denote a job/stage/step. 19 | 20 | ## Required Metadata for CD Events 21 | 22 | The following attributes are REQUIRED to be present in all the Events defined in this vocabulary: 23 | 24 | - **Event ID**: defines a unique identifier for the event 25 | - **Event Type**: defines a textual description of the event type, only event types described in this document are supported. All event types should be prefixed with `cd.` 26 | - **Event Source**: defines the context in which an event happened 27 | - **Event Timestamp**: defines the time when the event was produced 28 | 29 | The following sections list the events in different phases, allowing adopters to choose the events that they are more interested in. -------------------------------------------------------------------------------- /vocabulary-draft/source-code-version-control.md: -------------------------------------------------------------------------------- 1 | # Source Code Version Control Events 2 | 3 | __Note:__ This is a work-in-progress draft version and is being worked on by members of the Events SIG. You are very welcome to join the work and the discussions! 4 | 5 | This phase includes events related to changes in Source Code repositories that are relevant from a Continuous Delivery perspective. 6 | 7 | ## Repository Events 8 | 9 | These events are related to Source Code repositories 10 | - **Repository Created Event**: a new Source Code Repository was created to host source code for a project 11 | - **Repository Modified Event**: a Source Code Repository modified some of its attributes, like location, or owner 12 | - **Repository Deleted Event**: a Source Code Repository was deleted and it is not longer available 13 | - **Branch Created Event**: a Branch inside the Repository was created 14 | - **Branch Deleted Event**: a Branch inside the Repository was deleted 15 | 16 | 17 | 18 | Repository Events MUST include the following attributes: 19 | - **Event Type**: the type is restricted to include `cd.**` prefix. For example `cd.repository.created` or `cd.repository.changeapproved` 20 | - **Repository URL**: indicates the location of the source code repository for API operations, this URL needs to include the protocol used to connect to the repository. For example `git://` , `ssh://`, `https://` 21 | - **Repository Name**: friendly name to list this repository to users 22 | 23 | Optional attributes: 24 | - **Repository Owner**: indicates who is the owner of the repository, usually a `user` or an `organization` 25 | - **Repository View URL**: URL to access the repository from a user web browser 26 | 27 | From each repository you can emit events related with proposed source code changes. Each change can include a single or multiple commits that can also be tracked. 28 | 29 | - **Change Created Event**: a source code change was created and submitted to a repository specific branch. Examples: PullRequest sent to Github, MergeRequest sent to Gitlab, Change created in Gerrit 30 | - **Change Reviewed Event**: someone (user) or an automated system submitted an review to the source code change. A user or an automated system needs to be in charge of understanding how many approvals/rejections are needed for this change to be merged or rejected. The review event needs to include if the change is approved by the reviewer, more changes are needed or if the change is rejected. 31 | - **Change Merged Event**: the change is merged to the target branch where it was submitted. 32 | - **Change Abandoned Event**: a tool or a user decides that the change has been inactive for a while and it can be considered abandoned. 33 | - **Change Updated**: the Change has been updated, for example a new commit is added or removed from an existing Change 34 | 35 | 36 | Optional attributes for **Change** Events: 37 | - (TBD) 38 | --------------------------------------------------------------------------------