-
Notifications
You must be signed in to change notification settings - Fork 1
/
0eea26b0.15debcb9.js
1 lines (1 loc) · 20.4 KB
/
0eea26b0.15debcb9.js
1
(window.webpackJsonp=window.webpackJsonp||[]).push([[6],{134:function(e,t,n){"use strict";n.r(t),n.d(t,"frontMatter",(function(){return i})),n.d(t,"metadata",(function(){return l})),n.d(t,"rightToc",(function(){return s})),n.d(t,"default",(function(){return b}));var a=n(2),o=n(9),r=(n(0),n(179)),i={id:"shinyproxy-webhook",title:"CI/CD with Webhook"},l={id:"shinyproxy-webhook",title:"CI/CD with Webhook",description:"This guide explains how to set up webhook for ShinyProxy.",source:"@site/docs/shinyproxy-webhook.md",permalink:"/docs/shinyproxy-webhook",editUrl:"https://github.com/analythium/analythium.github.io/edit/source/docs/shinyproxy-webhook.md",lastUpdatedBy:"Peter Solymos",lastUpdatedAt:1675304109,sidebar:"docs",previous:{title:"Update ShinyProxy",permalink:"/docs/shinyproxy-update"}},s=[{value:"Port for webhook",id:"port-for-webhook",children:[]},{value:"Install webhook",id:"install-webhook",children:[]},{value:"Hook definitions",id:"hook-definitions",children:[{value:"Update all images",id:"update-all-images",children:[]},{value:"GitLab registry",id:"gitlab-registry",children:[]},{value:"Docker Hub",id:"docker-hub",children:[]}]},{value:"Webhook service",id:"webhook-service",children:[]},{value:"Enabling HTTPS",id:"enabling-https",children:[]},{value:"Testing with curl",id:"testing-with-curl",children:[{value:"GitLab",id:"gitlab",children:[]},{value:"Docker Hub",id:"docker-hub-1",children:[]}]},{value:"Wrapping up",id:"wrapping-up",children:[]}],c={rightToc:s};function b(e){var t=e.components,n=Object(o.a)(e,["components"]);return Object(r.b)("wrapper",Object(a.a)({},c,n,{components:t,mdxType:"MDXLayout"}),Object(r.b)("p",null,Object(r.b)("a",Object(a.a)({parentName:"p"},{href:"https://hosting.analythium.io/?utm_source=as-hub&utm_medium=web&utm_campaign=evergreen"}),Object(r.b)("img",Object(a.a)({parentName:"a"},{src:"https://hub.analythium.io/assets/marks/hosting-banner-2.jpg",alt:"Hosting Data Apps"})))),Object(r.b)("p",null,"This guide explains how to set up webhook for ShinyProxy.\nWe assume ShinyProxy is running on DigitalOcean droplet\nwith Ubuntu 20.04 (LTS)."),Object(r.b)("p",null,Object(r.b)("a",Object(a.a)({parentName:"p"},{href:"https://marketplace.digitalocean.com/apps/shinyproxy?refcode=a8041699739d"}),Object(r.b)("img",Object(a.a)({parentName:"a"},{src:"https://raw.githubusercontent.com/analythium/shinyproxy-1-click/master/digitalocean/images/do-btn-blue.svg",alt:"DO button"})))),Object(r.b)("h2",{id:"port-for-webhook"},"Port for webhook"),Object(r.b)("p",null,"UFW is an Uncomplicated Firewall.\nWe enables the UFW firewall to allow only SSH, HTTP and HTTPS.\nSee a detailed tutorial ",Object(r.b)("a",Object(a.a)({parentName:"p"},{href:"https://www.digitalocean.com/community/tutorials/how-to-set-up-a-firewall-with-ufw-on-ubuntu-20-04"}),"here"),".\nSettings we did ",Object(r.b)("a",Object(a.a)({parentName:"p"},{href:"shinyproxy-setup"}),"previously")," are commented out, uncomment\nas needed."),Object(r.b)("pre",null,Object(r.b)("code",Object(a.a)({parentName:"pre"},{className:"language-bash"}),"#sudo apt install ufw\n\n#sudo ufw default deny incoming\n#sudo ufw default allow outgoing\n\n#sudo ufw allow ssh\n#sudo ufw allow http\n#sudo ufw allow https\nsudo ufw allow 9000\n")),Object(r.b)("p",null,"Finally, enable these rules by running ",Object(r.b)("inlineCode",{parentName:"p"},"sudo ufw enable"),".\nCheck ",Object(r.b)("inlineCode",{parentName:"p"},"ufw status"),"."),Object(r.b)("h2",{id:"install-webhook"},"Install webhook"),Object(r.b)("p",null,"We are going to use ",Object(r.b)("a",Object(a.a)({parentName:"p"},{href:"https://github.com/adnanh/webhook"}),"webhook"),".\nThe community maintained ",Object(r.b)("inlineCode",{parentName:"p"},"sudo apt-get install webhook")," gives a really outdated version.\nTherefore we pick the latest (2.7.0) using pre-compiled binary for our\narchitecture (if in doubt, check ",Object(r.b)("inlineCode",{parentName:"p"},"dpkg --print-architecture"),"):"),Object(r.b)("pre",null,Object(r.b)("code",Object(a.a)({parentName:"pre"},{className:"language-bash"}),"sudo wget https://github.com/adnanh/webhook/releases/download/2.7.0/webhook-linux-amd64.tar.gz\ntar -zxvf webhook-linux-amd64.tar.gz\n")),Object(r.b)("p",null,"Next we will follow ",Object(r.b)("a",Object(a.a)({parentName:"p"},{href:"https://davidauthier.com/blog/2017/09/07/deploy-using-github-webhooks/"}),"this")," guide\nand we move the binary and other files with settings in the ",Object(r.b)("inlineCode",{parentName:"p"},"/var/www/webhooks")," directory:"),Object(r.b)("pre",null,Object(r.b)("code",Object(a.a)({parentName:"pre"},{className:"language-bash"}),"sudo mkdir /var/www/webhooks\nsudo cp webhook-linux-amd64/webhook /var/www/webhooks/\nrm -rf *\n")),Object(r.b)("h2",{id:"hook-definitions"},"Hook definitions"),Object(r.b)("p",null,"Create the file ",Object(r.b)("inlineCode",{parentName:"p"},"hooks.json")," to store the hook definitions:"),Object(r.b)("pre",null,Object(r.b)("code",Object(a.a)({parentName:"pre"},{className:"language-bash"}),"sudo touch /var/www/webhooks/hooks.json\n")),Object(r.b)("p",null,"The following array of hook definitions goes inside (",Object(r.b)("inlineCode",{parentName:"p"},"vim /var/www/webhooks/hooks.json"),"):"),Object(r.b)("pre",null,Object(r.b)("code",Object(a.a)({parentName:"pre"},{className:"language-json"}),'[\n {\n "id": "pull-all-gitlab",\n "execute-command": "webhook-pull-all-gitlab",\n "response-message": "Pulling all Docker images.",\n "response-headers":\n [\n {\n "name": "Access-Control-Allow-Origin",\n "value": "*"\n }\n ],\n "trigger-rule": {\n "match":\n {\n "type": "value",\n "value": "secret_token_1234",\n "parameter":\n {\n "source": "header",\n "name": "X-Gitlab-Token"\n }\n }\n }\n },\n {\n "id": "pull-one-gitlab",\n "execute-command": "webhook-pull-one-gitlab",\n "response-message": "Pulling Docker image.",\n "response-headers":\n [\n {\n "name": "Access-Control-Allow-Origin",\n "value": "*"\n }\n ],\n "pass-arguments-to-command": [\n {\n "source": "payload",\n "name": "image_name"\n }\n ],\n "trigger-rule": {\n "match":\n {\n "type": "value",\n "value": "secret_token_1234",\n "parameter":\n {\n "source": "header",\n "name": "X-Gitlab-Token"\n }\n }\n }\n },\n {\n "id": "pull-one-dockerhub",\n "execute-command": "webhook-pull-one-dockerhub",\n "response-message": "Pulling Docker image from Docker Hub.",\n "response-headers":\n [\n {\n "name": "Access-Control-Allow-Origin",\n "value": "*"\n }\n ],\n "pass-arguments-to-command": [\n {\n "source": "payload",\n "name": "repository.repo_name"\n },\n {\n "source": "payload",\n "name": "push_data.tag"\n }\n ]\n }\n]\n')),Object(r.b)("h3",{id:"update-all-images"},"Update all images"),Object(r.b)("p",null,"This array contains 3 hooks. The 1st and the second is\nset up to work with GitLab CI/CD pipelines.\nSee corresponding ",Object(r.b)("inlineCode",{parentName:"p"},".gitlab-ci.yml")," file ",Object(r.b)("a",Object(a.a)({parentName:"p"},{href:"https://gitlab.com/analythium/shinyproxy-hello/-/blob/master/.gitlab-ci.yml"}),"here")," (check parts that are commented out in the YAML file)."),Object(r.b)("p",null,"These need a secret header (value ",Object(r.b)("inlineCode",{parentName:"p"},'"secret_token_1234"'),")\nthat is used in the hook definition and in the webhook request. Change to some random\nhigh entropy value."),Object(r.b)("p",null,"The 1st hook definition calls the command ",Object(r.b)("inlineCode",{parentName:"p"},"webhook-pull-all-gitlab")," without arguments.\nThe command pulls the latest version of all the docker images that are on the server.\nAfter that, it cleans up the dangling images. So let's put this command into\nthe ",Object(r.b)("inlineCode",{parentName:"p"},"/bin")," folder and make it executable:"),Object(r.b)("pre",null,Object(r.b)("code",Object(a.a)({parentName:"pre"},{className:"language-bash"}),"sudo touch /bin/webhook-pull-all-gitlab\nchmod 755 /bin/webhook-pull-all-gitlab\n")),Object(r.b)("p",null,"This is the content that goes inside the file:"),Object(r.b)("pre",null,Object(r.b)("code",Object(a.a)({parentName:"pre"},{className:"language-bash"}),"#! /bin/sh\ndocker images |grep -v REPOSITORY|awk '{print $1\":\"$2}'|xargs -L1 docker pull\ndocker system prune -f\n")),Object(r.b)("p",null,Object(r.b)("inlineCode",{parentName:"p"},"docker login")," might be needed when using private registries."),Object(r.b)("h3",{id:"gitlab-registry"},"GitLab registry"),Object(r.b)("p",null,"The second hook definition uses the command ",Object(r.b)("inlineCode",{parentName:"p"},"webhook-pull-one-gitlab")," which\npulls a single image based on the argument passed."),Object(r.b)("pre",null,Object(r.b)("code",Object(a.a)({parentName:"pre"},{className:"language-bash"}),"sudo touch /bin/webhook-pull-one-gitlab\nchmod 755 /bin/webhook-pull-one-gitlab\n")),Object(r.b)("p",null,"The content of the file:"),Object(r.b)("pre",null,Object(r.b)("code",Object(a.a)({parentName:"pre"},{className:"language-bash"}),"#! /bin/sh\ndocker pull $1\ndocker system prune -f\n")),Object(r.b)("h3",{id:"docker-hub"},"Docker Hub"),Object(r.b)("p",null,"The 3rd hook definition is similar the previous hook in that it also pulls a single\ndocker image. But this one is written for the payload that Docker Hub's webhook\ndelivers (read more ",Object(r.b)("a",Object(a.a)({parentName:"p"},{href:"https://docs.docker.com/docker-hub/webhooks/#example-webhook-payload"}),"here"),")."),Object(r.b)("p",null,"The image name and the tag are parsed separately, so the ",Object(r.b)("inlineCode",{parentName:"p"},"webhook-pull-one-dockerhub"),"\ntakes these two arguments:"),Object(r.b)("pre",null,Object(r.b)("code",Object(a.a)({parentName:"pre"},{className:"language-bash"}),"sudo touch /bin/webhook-pull-one-dockerhub\nchmod 755 /bin/webhook-pull-one-dockerhub\n")),Object(r.b)("pre",null,Object(r.b)("code",Object(a.a)({parentName:"pre"},{className:"language-bash"}),"#! /bin/sh\n/usr/bin/docker pull $1:$2\n/usr/bin/docker system prune -f\n")),Object(r.b)("h2",{id:"webhook-service"},"Webhook service"),Object(r.b)("p",null,"Now create the ",Object(r.b)("inlineCode",{parentName:"p"},"webhook.service")," file with the daemon settings via ",Object(r.b)("inlineCode",{parentName:"p"},"systemctl"),":"),Object(r.b)("pre",null,Object(r.b)("code",Object(a.a)({parentName:"pre"},{className:"language-bash"}),"sudo touch /etc/systemd/system/webhook.service\n")),Object(r.b)("p",null,"Put these into the service file (",Object(r.b)("inlineCode",{parentName:"p"},"vim /etc/systemd/system/webhook.service"),"):"),Object(r.b)("pre",null,Object(r.b)("code",Object(a.a)({parentName:"pre"},{className:"language-vim"}),"[Unit]\nDescription=Webhooks\n\n[Service]\nExecStart=/var/www/webhooks/webhook -hooks /var/www/webhooks/hooks.json -hotreload\n\n[Install]\nWantedBy=multi-user.target\n")),Object(r.b)("p",null,"The option ",Object(r.b)("inlineCode",{parentName:"p"},"-hotreload")," watches for changes in the ",Object(r.b)("inlineCode",{parentName:"p"},"hook.json")," file and reloads them upon change."),Object(r.b)("p",null,"Run a few commands with ",Object(r.b)("inlineCode",{parentName:"p"},"systemctl"),":\n",Object(r.b)("inlineCode",{parentName:"p"},"sudo systemctl enable webhook.service")," to enable the newly created service,\n",Object(r.b)("inlineCode",{parentName:"p"},"sudo systemctl start webhook.service")," to start the service."),Object(r.b)("p",null,"Now check the service status using ",Object(r.b)("inlineCode",{parentName:"p"},"sudo service webhook status"),". If all went well,\nyou should see something like:"),Object(r.b)("pre",null,Object(r.b)("code",Object(a.a)({parentName:"pre"},{className:"language-bash"}),"\u25cf webhook.service - Webhooks\n Loaded: loaded (/etc/systemd/system/webhook.service; enabled; vendor preset: enabled)\n Active: active (running) since Fri 2020-06-05 07:31:31 UTC; 6s ago\n Main PID: 5228 (webhook)\n Tasks: 6 (limit: 1152)\n CGroup: /system.slice/webhook.service\n \u2514\u25005228 /var/www/webhooks/webhook -hooks /var/www/webhooks/hooks.json -hotreload\n")),Object(r.b)("h2",{id:"enabling-https"},"Enabling HTTPS"),Object(r.b)("p",null,"Add ",Object(r.b)("inlineCode",{parentName:"p"},"-secure")," flag to watch over https. This requires also passing the certificate:\ncheck name of certificate and private key in the dir ",Object(r.b)("inlineCode",{parentName:"p"},"/etc/letsencrypt/live/example.com/"),",\nthe add ",Object(r.b)("inlineCode",{parentName:"p"},"-secure -cert /etc/letsencrypt/live/test.side-r.com/cert.pem -key /etc/letsencrypt/live/test.side-r.com/privkey.pem")," to the ",Object(r.b)("inlineCode",{parentName:"p"},"/etc/systemd/system/webhook.service")," service file.\nUse private key (",Object(r.b)("inlineCode",{parentName:"p"},"privkey.pem"),") and ",Object(r.b)("inlineCode",{parentName:"p"},"fullchain.pem")," which is concatenation of the public key\n(",Object(r.b)("inlineCode",{parentName:"p"},"cert.pem"),") and the certificate chain (",Object(r.b)("inlineCode",{parentName:"p"},"chain.pem"),")."),Object(r.b)("p",null,"Use ",Object(r.b)("inlineCode",{parentName:"p"},"crontab -e")," and add the line ",Object(r.b)("inlineCode",{parentName:"p"},"0 2 * * * systemctl restart webhook.service"),":\nwe need to restart the webhook daemon regularly (daily in this case)\nbecause it is not updating when the TLS certificate is renewed."),Object(r.b)("h2",{id:"testing-with-curl"},"Testing with curl"),Object(r.b)("p",null,"Test it in ",Object(r.b)("inlineCode",{parentName:"p"},"-verbose")," mode: change ",Object(r.b)("inlineCode",{parentName:"p"},"example.com")," to your domain.\nHave to open up another port, here 9001, because 9000 is taken by the daemon:\n",Object(r.b)("inlineCode",{parentName:"p"},"/var/www/webhooks/webhook -hooks /var/www/webhooks/hooks.json -hotreload -verbose -secure -cert /etc/letsencrypt/live/test.side-r.com/fullchain.pem -key /etc/letsencrypt/live/test.side-r.com/privkey.pem -port 9001")),Object(r.b)("p",null,"See more parameter settings ",Object(r.b)("a",Object(a.a)({parentName:"p"},{href:"https://github.com/adnanh/webhook/blob/master/docs/Webhook-Parameters.md"}),"here"),"."),Object(r.b)("p",null,"Note: we are testing over port 9001, but the real webhook is listening on port 9000."),Object(r.b)("h3",{id:"gitlab"},"GitLab"),Object(r.b)("p",null,"We use ",Object(r.b)("inlineCode",{parentName:"p"},"curl -i")," to get the response headers: 200 is what we want. Make sure to use http\nprotocol (and not https) if SSL certificate is not set up and used."),Object(r.b)("pre",null,Object(r.b)("code",Object(a.a)({parentName:"pre"},{className:"language-bash"}),'curl -i --header "X-Gitlab-Token: secret_token_1234" https://YOUR_IP_OR_DOMAIN:9000/hooks/pull-all-gitlab\n')),Object(r.b)("p",null,'Using form data (url encoded, default header "Content-Type: application/x-www-form-urlencoded"):'),Object(r.b)("pre",null,Object(r.b)("code",Object(a.a)({parentName:"pre"},{className:"language-bash"}),"curl -i --header \"X-Gitlab-Token: secret_token_1234\" \\\n -X POST -d 'image_name=analythium/shinyproxy-demo:latest' \\\n https://YOUR_IP_OR_DOMAIN:9000/hooks/pull-one-gitlab\n")),Object(r.b)("p",null,"Need to declare content-type header, payload is treated as form data by curl:"),Object(r.b)("pre",null,Object(r.b)("code",Object(a.a)({parentName:"pre"},{className:"language-bash"}),'curl -i --header "X-Gitlab-Token: secret_token_1234" \\\n --header "Content-Type: application/json" \\\n --request POST \\\n --data \'{"image_name":"analythium/shinyproxy-demo:latest"}\' \\\n https://YOUR_IP_OR_DOMAIN:9000/hooks/pull-one-gitlab\n')),Object(r.b)("h3",{id:"docker-hub-1"},"Docker Hub"),Object(r.b)("p",null,"This is how the simplified Docker Hub payload looks like, we can use it to get the\nimage name and the tag:"),Object(r.b)("pre",null,Object(r.b)("code",Object(a.a)({parentName:"pre"},{className:"language-json"}),'{\n "push_data": {\n "pusher": "trustedbuilder",\n "tag": "latest"\n },\n "repository": {\n "name": "testhook",\n "namespace": "svendowideit",\n "owner": "svendowideit",\n "repo_name": "svendowideit/testhook",\n "repo_url": "https://registry.hub.docker.com/u/svendowideit/testhook/",\n "star_count": 0,\n "status": "Active"\n }\n}\n')),Object(r.b)("p",null,"Set webhook url as ",Object(r.b)("inlineCode",{parentName:"p"},"https://YOUR_IP_OR_DOMAIN:9000/hooks/pull-one-dockerhub"),"."),Object(r.b)("h2",{id:"wrapping-up"},"Wrapping up"),Object(r.b)("p",null,"At the end of all this, we have the full CI/CD experience over HTTPS:"),Object(r.b)("p",null,Object(r.b)("img",Object(a.a)({parentName:"p"},{src:"../../img/shinyproxy/workflow.png",alt:"CI/CD workflow",title:"CI/CD workflow"}))),Object(r.b)("div",{className:"admonition admonition-tip alert alert--success"},Object(r.b)("div",Object(a.a)({parentName:"div"},{className:"admonition-heading"}),Object(r.b)("h5",{parentName:"div"},Object(r.b)("span",Object(a.a)({parentName:"h5"},{className:"admonition-icon"}),Object(r.b)("svg",Object(a.a)({parentName:"span"},{xmlns:"http://www.w3.org/2000/svg",width:"12",height:"16",viewBox:"0 0 12 16"}),Object(r.b)("path",Object(a.a)({parentName:"svg"},{fillRule:"evenodd",d:"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"})))),"Contact us!")),Object(r.b)("div",Object(a.a)({parentName:"div"},{className:"admonition-content"}),Object(r.b)("p",{parentName:"div"},"Would you like to run your own ShinyProxy server with CICD pipelines? Reach out to ",Object(r.b)("a",Object(a.a)({parentName:"p"},{href:"https://analythium.io/#contact"}),"Analythium")," if you need commercial support and consulting services!"))),Object(r.b)("p",null,Object(r.b)("a",Object(a.a)({parentName:"p"},{href:"https://hosting.analythium.io/?utm_source=as-hub&utm_medium=web&utm_campaign=evergreen"}),Object(r.b)("img",Object(a.a)({parentName:"a"},{src:"https://hub.analythium.io/assets/marks/hosting-banner-2.jpg",alt:"Hosting Data Apps"})))))}b.isMDXComponent=!0},179:function(e,t,n){"use strict";n.d(t,"a",(function(){return p})),n.d(t,"b",(function(){return d}));var a=n(0),o=n.n(a);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function l(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?i(Object(n),!0).forEach((function(t){r(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):i(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,a,o=function(e,t){if(null==e)return{};var n,a,o={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var c=o.a.createContext({}),b=function(e){var t=o.a.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},p=function(e){var t=b(e.components);return o.a.createElement(c.Provider,{value:t},e.children)},h={inlineCode:"code",wrapper:function(e){var t=e.children;return o.a.createElement(o.a.Fragment,{},t)}},u=o.a.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,i=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),p=b(n),u=a,d=p["".concat(i,".").concat(u)]||p[u]||h[u]||r;return n?o.a.createElement(d,l(l({ref:t},c),{},{components:n})):o.a.createElement(d,l({ref:t},c))}));function d(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,i=new Array(r);i[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var c=2;c<r;c++)i[c]=n[c];return o.a.createElement.apply(null,i)}return o.a.createElement.apply(null,n)}u.displayName="MDXCreateElement"}}]);