├── .github └── workflows │ └── build.yaml ├── .gitignore ├── LICENSE ├── README.markdown ├── build.gradle ├── buildSrc ├── build.gradle └── src │ └── main │ └── groovy │ ├── GenerateDocumentation.groovy │ └── NebulaRelease.groovy ├── docs ├── .nojekyll ├── 404.html ├── assets │ ├── css │ │ └── styles.fb53c356.css │ ├── images │ │ └── vmware-test-case-flow-5b80eaa38ddb8f6d28abf2aa5f5295bd.jpg │ └── js │ │ ├── 01a85c17.201a70b5.js │ │ ├── 0aa62002.6e47bd2d.js │ │ ├── 0e384e19.61658283.js │ │ ├── 15bd92de.6623578a.js │ │ ├── 17896441.c15c396c.js │ │ ├── 1be78505.ef0ee4d7.js │ │ ├── 1d48e3ab.8ff3c9fc.js │ │ ├── 34a804db.93cce336.js │ │ ├── 3720c009.46d24c30.js │ │ ├── 508a7d2f.43c89018.js │ │ ├── 54f44165.5cf5c70a.js │ │ ├── 55960ee5.72573837.js │ │ ├── 65664f72.2e7a62fc.js │ │ ├── 6875c492.2566d241.js │ │ ├── 74c14969.f1b0f9db.js │ │ ├── 75.8fe7757f.js │ │ ├── 763fcd15.95a8f898.js │ │ ├── 814f3328.db8f94e4.js │ │ ├── 845.9544dcf2.js │ │ ├── 8965b357.5ced651d.js │ │ ├── 935f2afb.11666b9e.js │ │ ├── 9945d209.f9563fd6.js │ │ ├── 9e4087bc.e0a9a0f2.js │ │ ├── a22865bc.9bd4aa3d.js │ │ ├── a2762952.6bf692cf.js │ │ ├── a6aa9e1f.04e0c149.js │ │ ├── ae0f9d55.f107f8ab.js │ │ ├── b3ff6462.3df797cc.js │ │ ├── c4f5d8e4.3c135bce.js │ │ ├── ccc49370.8f94f054.js │ │ ├── common.982779c9.js │ │ ├── common.982779c9.js.LICENSE.txt │ │ ├── main.6f20bb18.js │ │ ├── main.6f20bb18.js.LICENSE.txt │ │ └── runtime~main.48a448b1.js ├── blog │ ├── 2021 │ │ └── 09 │ │ │ └── 15 │ │ │ └── how-to-run-integration-tests-against-vmware │ │ │ └── index.html │ ├── archive │ │ └── index.html │ ├── atom.xml │ ├── index.html │ ├── rss.xml │ └── tags │ │ ├── index.html │ │ ├── integration-test │ │ └── index.html │ │ ├── overcast │ │ └── index.html │ │ └── vmware │ │ └── index.html ├── docs │ ├── getting-started │ │ ├── development │ │ │ └── index.html │ │ ├── docker │ │ │ └── index.html │ │ ├── examples │ │ │ └── index.html │ │ ├── features │ │ │ └── index.html │ │ ├── installation │ │ │ └── index.html │ │ ├── libvirt │ │ │ └── index.html │ │ └── properties │ │ │ └── index.html │ ├── intro │ │ └── index.html │ └── tags │ │ └── index.html ├── img │ ├── 1.svg │ ├── 2.svg │ ├── 3.svg │ └── digital_ai_deploy.svg ├── index.html └── sitemap.xml ├── documentation ├── .gitignore ├── babel.config.js ├── blog │ ├── 2021-09-15-how-to-run-integration-tests-against-vmware.md │ └── pics │ │ └── vmware │ │ └── vmware-test-case-flow.jpg ├── docs │ ├── getting-started │ │ ├── _category_.json │ │ ├── development.md │ │ ├── docker.md │ │ ├── examples.md │ │ ├── features.md │ │ ├── installation.md │ │ ├── libvirt.md │ │ └── properties.md │ └── intro.md ├── docusaurus.config.js ├── package.json ├── sidebars.js ├── src │ ├── components │ │ ├── HomepageFeatures.js │ │ └── HomepageFeatures.module.css │ ├── css │ │ └── custom.css │ └── pages │ │ ├── index.js │ │ └── index.module.css ├── static │ ├── .nojekyll │ └── img │ │ ├── 1.svg │ │ ├── 2.svg │ │ ├── 3.svg │ │ └── digital_ai_deploy.svg └── yarn.lock ├── gradle.properties ├── gradle ├── license-header └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── itest ├── overcast-docker-itest │ ├── build.gradle │ └── src │ │ └── test │ │ ├── java │ │ └── com │ │ │ └── xebialabs │ │ │ └── overcast │ │ │ └── host │ │ │ └── DockerHostItest.java │ │ └── resources │ │ └── overcast.conf ├── overcast-libvirt-itest │ ├── build.gradle │ └── src │ │ └── test │ │ ├── java │ │ └── com │ │ │ └── xebialabs │ │ │ └── overcast │ │ │ └── host │ │ │ └── LibVirtHostItest.java │ │ └── resources │ │ └── overcast.conf ├── overcast-vagrant-itest │ ├── box │ │ └── Vagrantfile │ ├── build.gradle │ └── src │ │ └── test │ │ ├── java │ │ └── com │ │ │ └── xebialabs │ │ │ └── overcast │ │ │ └── host │ │ │ └── CachedVagrantCloudHostItest.java │ │ └── resources │ │ └── overcast.conf └── overcast-vmware-itest │ ├── build.gradle │ └── src │ └── test │ ├── java │ └── com │ │ └── xebialabs │ │ └── overcast │ │ └── host │ │ └── VMWareCloudHostItest.java │ └── resources │ └── overcast.conf ├── overcast.conf ├── settings.gradle └── src ├── main └── java │ └── com │ └── xebialabs │ └── overcast │ ├── OvercastProperties.java │ ├── OverthereUtil.java │ ├── Preconditions.java │ ├── PropertiesLoader.java │ ├── Resources.java │ ├── Strings.java │ ├── command │ ├── Command.java │ ├── CommandProcessor.java │ ├── CommandResponse.java │ └── NonZeroCodeException.java │ ├── host │ ├── CachedLibvirtHost.java │ ├── CachedVagrantCloudHost.java │ ├── CloudHost.java │ ├── CloudHostFactory.java │ ├── DockerHost.java │ ├── Ec2CloudHost.java │ ├── ExistingCloudHost.java │ ├── LibvirtHost.java │ ├── TunneledCloudHost.java │ ├── VMWareHost.java │ ├── VagrantCloudHost.java │ └── VirtualboxHost.java │ ├── support │ ├── docker │ │ ├── Config.java │ │ ├── DockerDriver.java │ │ └── ProcessHandlerLogger.java │ ├── libvirt │ │ ├── Disk.java │ │ ├── DomainWrapper.java │ │ ├── Filesystem.java │ │ ├── IpLookupException.java │ │ ├── IpLookupStrategy.java │ │ ├── IpNotFoundException.java │ │ ├── JDomUtil.java │ │ ├── LibvirtRuntimeException.java │ │ ├── LibvirtUtil.java │ │ ├── LoggingOutputHandler.java │ │ ├── Metadata.java │ │ ├── SshIpLookupStrategy.java │ │ ├── StaticIpLookupStrategy.java │ │ └── jdom │ │ │ ├── DiskXml.java │ │ │ ├── DomainXml.java │ │ │ ├── FilesystemXml.java │ │ │ └── InterfaceXml.java │ ├── vagrant │ │ ├── VagrantDriver.java │ │ └── VagrantState.java │ ├── virtualbox │ │ ├── VirtualboxDriver.java │ │ └── VirtualboxState.java │ └── vmware │ │ ├── JsonBodyHandler.java │ │ ├── VMWareVM.java │ │ └── VmWareApiClient.java │ └── util │ └── RetryCommand.java └── test ├── java └── com │ └── xebialabs │ └── overcast │ ├── OvercastPropertiesTest.java │ ├── OverthereUtilTest.java │ ├── PropertiesLoaderTest.java │ ├── command │ └── CommandProcessorTest.java │ ├── host │ ├── CachedLibvirtHostTest.java │ ├── CachedVagrantCloudHostTest.java │ └── DockerHostTest.java │ └── support │ ├── libvirt │ ├── MetadataTest.java │ └── jdom │ │ ├── DomainXmlTest.java │ │ └── FilesystemXmlTest.java │ ├── vagrant │ ├── VagrantCloudHostTest.java │ └── VagrantStateTest.java │ └── virtualbox │ ├── VirtualboxDriverTest.java │ └── VirtualboxStateTest.java └── resources ├── copyFilesTest ├── 1 │ └── file └── 2 │ ├── file2 │ └── sub │ └── file3 ├── dir-without-conf └── no-config-here ├── fake-home └── .overcast │ └── overcast.conf ├── libvirt-xml ├── domain-with-filesystem.xml └── simple-domain.xml ├── logback-test.xml ├── overcast.conf ├── property-path └── overcast.conf └── with space └── test.conf /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Run integration server 2 | on: [push] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - name: Set up JDK 11 9 | uses: actions/setup-java@v2 10 | with: 11 | java-version: '11' 12 | distribution: 'adopt' 13 | - name: Validate Gradle wrapper 14 | uses: gradle/wrapper-validation-action@e6e38bacfdf1a337459f332974bb2327a31aaf4b 15 | - name: Build with Gradle 16 | run: ./gradlew clean build 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vagrant 2 | .classpath 3 | .project 4 | .settings 5 | .gradle 6 | bin 7 | build 8 | *.iml 9 | *.iws 10 | *.ipr 11 | .idea/ 12 | classes/ 13 | out/ 14 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Overcast 2 | 3 | Full documentation can be found by this link: 4 | [https://xebialabs.github.io/overcast](https://xebialabs.github.io/overcast/) 5 | -------------------------------------------------------------------------------- /buildSrc/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'groovy' 3 | } 4 | 5 | def projectProperties = new Properties() 6 | file("${rootDir.parentFile}/gradle.properties").withInputStream { InputStream s -> 7 | projectProperties.load(s) 8 | } 9 | 10 | sourceCompatibility = projectProperties['languageLevel'] 11 | targetCompatibility = projectProperties['languageLevel'] 12 | -------------------------------------------------------------------------------- /buildSrc/src/main/groovy/GenerateDocumentation.groovy: -------------------------------------------------------------------------------- 1 | import org.gradle.api.Action 2 | import org.gradle.api.DefaultTask 3 | import org.gradle.api.tasks.TaskAction 4 | import org.gradle.process.ExecSpec 5 | 6 | class GenerateDocumentation extends DefaultTask { 7 | 8 | @TaskAction 9 | void doRelease() { 10 | getProject().exec(new Action() { 11 | @Override 12 | void execute(ExecSpec execSpec) { 13 | project.logger.lifecycle("Generating documentation from markdown files") 14 | 15 | execSpec.executable('./gradlew') 16 | execSpec.args( 17 | "commitChanges", 18 | "-PgitBranchName=master", 19 | "-PgitMessage=Documentation has been updated", 20 | "-PgitFileContent=docs/*" 21 | ) 22 | } 23 | }) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /buildSrc/src/main/groovy/NebulaRelease.groovy: -------------------------------------------------------------------------------- 1 | import org.gradle.api.Action 2 | import org.gradle.api.DefaultTask 3 | import org.gradle.api.tasks.TaskAction 4 | import org.gradle.process.ExecSpec 5 | 6 | class NebulaRelease extends DefaultTask { 7 | 8 | @TaskAction 9 | void doRelease() { 10 | getProject().exec(new Action() { 11 | @Override 12 | void execute(ExecSpec execSpec) { 13 | def version = project.releasedVersion 14 | project.logger.lifecycle("Releasing version is: $version") 15 | 16 | execSpec.executable('./gradlew') 17 | execSpec.args('build', 'uploadArchives', "-Prelease.version=$version", "final", 18 | "-Prelease.ignoreSuppliedVersionVerification=true") 19 | } 20 | }) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xebialabs/overcast/8a494dc8f8572ae337ce91b06829eb9567c91f5d/docs/.nojekyll -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Page Not Found | Overcast 9 | 10 | 11 | 12 | 13 |
14 |
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /docs/assets/images/vmware-test-case-flow-5b80eaa38ddb8f6d28abf2aa5f5295bd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xebialabs/overcast/8a494dc8f8572ae337ce91b06829eb9567c91f5d/docs/assets/images/vmware-test-case-flow-5b80eaa38ddb8f6d28abf2aa5f5295bd.jpg -------------------------------------------------------------------------------- /docs/assets/js/01a85c17.201a70b5.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[13],{6165:function(e,t,n){n.d(t,{Z:function(){return b}});var r=n(3366),a=n(7294),l=n(6010),c=n(6698),i=n(6742),s="sidebar_2ahu",o="sidebarItemTitle_2hhb",u="sidebarItemList_2xAf",m="sidebarItem_2UVv",f="sidebarItemLink_1RT6",d="sidebarItemLinkActive_12pM",v=n(4973);function h(e){var t=e.sidebar;return 0===t.items.length?null:a.createElement("nav",{className:(0,l.Z)(s,"thin-scrollbar"),"aria-label":(0,v.I)({id:"theme.blog.sidebar.navAriaLabel",message:"Blog recent posts navigation",description:"The ARIA label for recent posts in the blog sidebar"})},a.createElement("div",{className:(0,l.Z)(o,"margin-bottom--md")},t.title),a.createElement("ul",{className:u},t.items.map((function(e){return a.createElement("li",{key:e.permalink,className:m},a.createElement(i.Z,{isNavLink:!0,to:e.permalink,className:f,activeClassName:d},e.title))}))))}var g=n(571),E=["sidebar","toc","children"];var b=function(e){var t=e.sidebar,n=e.toc,i=e.children,s=(0,r.Z)(e,E),o=t&&t.items.length>0;return a.createElement(c.Z,s,a.createElement("div",{className:"container margin-vert--lg"},a.createElement("div",{className:"row"},o&&a.createElement("aside",{className:"col col--3"},a.createElement(h,{sidebar:t})),a.createElement("main",{className:(0,l.Z)("col",{"col--7":o,"col--9 col--offset-1":!o}),itemScope:!0,itemType:"http://schema.org/Blog"},i),n&&a.createElement("div",{className:"col col--2"},a.createElement(g.Z,{toc:n})))))}},94:function(e,t,n){n.r(t);var r=n(7294),a=n(6165),l=n(6584),c=n(941);t.default=function(e){var t=e.tags,n=e.sidebar,i=(0,c.MA)();return r.createElement(a.Z,{title:i,wrapperClassName:c.kM.wrapper.blogPages,pageClassName:c.kM.page.blogTagsListPage,searchMetadatas:{tag:"blog_tags_list"},sidebar:n},r.createElement("h1",null,i),r.createElement(l.Z,{tags:Object.values(t)}))}},571:function(e,t,n){n.d(t,{r:function(){return d},Z:function(){return v}});var r=n(7294),a=n(6010),l=n(941);function c(e){var t=e.getBoundingClientRect();return t.top===t.bottom?c(e.parentNode):t}function i(e){var t,n=e.anchorTopOffset,r=Array.from(document.querySelectorAll(".anchor.anchor__h2, .anchor.anchor__h3")),a=r.find((function(e){return c(e).top>=n}));return a?function(e){return e.top>0&&e.bottom= 4.2"),(0,r.kt)("li",{parentName:"ul"},"Vagrant version >= 1.2.7"),(0,r.kt)("li",{parentName:"ul"},"Qemu/KVM version that supports domain metadata (QEMU-KVM 1.4.2 (Fedora 19), 2.0.0 (Ubuntu LTS 14))"),(0,r.kt)("li",{parentName:"ul"},"Docker >= 1.6"),(0,r.kt)("li",{parentName:"ul"},"VMWare >= 7.0")),(0,r.kt)("h2",{id:"from-maven-repo"},"From maven repo"),(0,r.kt)("p",null,(0,r.kt)("a",{parentName:"p",href:"http://mvnrepository.com/artifact/com.xebialabs.cloud/overcast/2.5.1"},"http://mvnrepository.com/artifact/com.xebialabs.cloud/overcast/2.5.1")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"\n com.xebialabs.cloud\n overcast\n 2.5.1\n\n")),(0,r.kt)("p",null,"Note: the libvirt JNA wrapper may require adding the libvirt.org repository to your build: ",(0,r.kt)("a",{parentName:"p",href:"http://www.libvirt.org/maven2/"},"http://www.libvirt.org/maven2/")),(0,r.kt)("h2",{id:"from-sources"},"From sources"),(0,r.kt)("p",null," ",(0,r.kt)("inlineCode",{parentName:"p"},"gradle build")),(0,r.kt)("h2",{id:"usage"},"Usage"),(0,r.kt)("h3",{id:"set-up-your-host"},"Set up your host"),(0,r.kt)("p",null,"Overcast looks for configuration properties in this order:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"System.getProperty()")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"/.overcast/overcast.conf")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"/overcast.conf")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"/overcast.conf"))),(0,r.kt)("div",{className:"admonition admonition-note alert alert--secondary"},(0,r.kt)("div",{parentName:"div",className:"admonition-heading"},(0,r.kt)("h5",{parentName:"div"},(0,r.kt)("span",{parentName:"h5",className:"admonition-icon"},(0,r.kt)("svg",{parentName:"span",xmlns:"http://www.w3.org/2000/svg",width:"14",height:"16",viewBox:"0 0 14 16"},(0,r.kt)("path",{parentName:"svg",fillRule:"evenodd",d:"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"}))),"note")),(0,r.kt)("div",{parentName:"div",className:"admonition-content"},(0,r.kt)("p",{parentName:"div"},"The home location takes precedence over the project location.",(0,r.kt)("br",null),"This allows developers to adapt settings to their local setup without changing the project defaults."))),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"overcast.conf")," files are in ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/typesafehub/config#using-hocon-the-json-superset"},"Typesafe Config HOCON syntax"),".",(0,r.kt)("br",null),"This is a flexible JSON superset that allows comments, substitution, file inclusion, and more."))}d.isMDXComponent=!0}}]); -------------------------------------------------------------------------------- /docs/assets/js/55960ee5.72573837.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[121],{8070:function(e){e.exports=[]}}]); -------------------------------------------------------------------------------- /docs/assets/js/65664f72.2e7a62fc.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[5],{8893:function(a){a.exports=JSON.parse('{"allTagsPath":"/overcast/blog/tags","slug":"/overcast/blog/tags/overcast","name":"overcast","count":1,"permalink":"/overcast/blog/tags/overcast"}')}}]); -------------------------------------------------------------------------------- /docs/assets/js/74c14969.f1b0f9db.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[761],{6226:function(e,t,n){n.r(t),n.d(t,{frontMatter:function(){return c},contentTitle:function(){return s},metadata:function(){return l},toc:function(){return d},default:function(){return u}});var r=n(7462),o=n(3366),i=(n(7294),n(3905)),a=["components"],c={sidebar_position:5},s="Docker",l={unversionedId:"getting-started/docker",id:"getting-started/docker",isDocsHomePage:!1,title:"Docker",description:"Docker concepts",source:"@site/docs/getting-started/docker.md",sourceDirName:"getting-started",slug:"/getting-started/docker",permalink:"/overcast/docs/getting-started/docker",tags:[],version:"current",sidebarPosition:5,frontMatter:{sidebar_position:5},sidebar:"tutorialSidebar",previous:{title:"Libvirt",permalink:"/overcast/docs/getting-started/libvirt"},next:{title:"Examples",permalink:"/overcast/docs/getting-started/examples"}},d=[{value:"Docker concepts",id:"docker-concepts",children:[]}],p={toc:d};function u(e){var t=e.components,n=(0,o.Z)(e,a);return(0,i.kt)("wrapper",(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"docker"},"Docker"),(0,i.kt)("h2",{id:"docker-concepts"},"Docker concepts"),(0,i.kt)("p",null,"During ",(0,i.kt)("inlineCode",{parentName:"p"},"setup()"),", Overcast will create and start a new Docker container. If the image specified is not available in the local registry, it will be automatically pulled from the central Docker repository."),(0,i.kt)("p",null,"During ",(0,i.kt)("inlineCode",{parentName:"p"},"teardown()"),", it will stop the container and optionally remove the container (see remove property)."),(0,i.kt)("p",null,"Calling ",(0,i.kt)("inlineCode",{parentName:"p"},"getHostName()")," will return the hostname of the Docker Host, assuming the container will run on that host, with the exposed ports accessible on the Docker host."),(0,i.kt)("p",null,"Calling ",(0,i.kt)("inlineCode",{parentName:"p"},"getPort(port)")," will translate the internal port (passed as an argument) to the port externally exposed by the Docker Container. The port number is dynamically determined by Docker. The port range used for dynamic allocation is 49153 to 65535 (defined by Docker)."),(0,i.kt)("p",null,"We use the ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/spotify/docker-client"},"Spotify Docker Client")," library."))}u.isMDXComponent=!0}}]); -------------------------------------------------------------------------------- /docs/assets/js/75.8fe7757f.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[75],{4608:function(e,t,n){n.r(t);var a=n(7294),o=n(6698),l=n(4973);t.default=function(){return a.createElement(o.Z,{title:(0,l.I)({id:"theme.NotFound.title",message:"Page Not Found"})},a.createElement("main",{className:"container margin-vert--xl"},a.createElement("div",{className:"row"},a.createElement("div",{className:"col col--6 col--offset-3"},a.createElement("h1",{className:"hero__title"},a.createElement(l.Z,{id:"theme.NotFound.title",description:"The title of the 404 page"},"Page Not Found")),a.createElement("p",null,a.createElement(l.Z,{id:"theme.NotFound.p1",description:"The first paragraph of the 404 page"},"We could not find what you were looking for.")),a.createElement("p",null,a.createElement(l.Z,{id:"theme.NotFound.p2",description:"The 2nd paragraph of the 404 page"},"Please contact the owner of the site that linked you to the original URL and let them know their link is broken."))))))}}}]); -------------------------------------------------------------------------------- /docs/assets/js/763fcd15.95a8f898.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[380],{6879:function(t){t.exports=JSON.parse('{"blogPosts":[{"id":"How to run integration tests against VM Ware","metadata":{"permalink":"/overcast/blog/2021/09/15/how-to-run-integration-tests-against-vmware","source":"@site/blog/2021-09-15-how-to-run-integration-tests-against-vmware.md","title":"How to run integration tests against VM Ware","description":"This article is for you, if you have to run VMs for your integration tests, and you use as a hypervisor - VM Ware 7.0","date":"2021-09-15T00:00:00.000Z","formattedDate":"September 15, 2021","tags":[{"label":"overcast","permalink":"/overcast/blog/tags/overcast"},{"label":"vmware","permalink":"/overcast/blog/tags/vmware"},{"label":"integration-test","permalink":"/overcast/blog/tags/integration-test"}],"readingTime":2.745,"truncated":false,"authors":[]}}]}')}}]); -------------------------------------------------------------------------------- /docs/assets/js/814f3328.db8f94e4.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[535],{5641:function(t){t.exports=JSON.parse('{"title":"Recent posts","items":[{"title":"How to run integration tests against VM Ware","permalink":"/overcast/blog/2021/09/15/how-to-run-integration-tests-against-vmware"}]}')}}]); -------------------------------------------------------------------------------- /docs/assets/js/935f2afb.11666b9e.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[53],{1109:function(e){e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"link","label":"Introduction","href":"/overcast/docs/intro"},{"type":"category","label":"Getting Started","items":[{"type":"link","label":"Installation","href":"/overcast/docs/getting-started/installation"},{"type":"link","label":"Features","href":"/overcast/docs/getting-started/features"},{"type":"link","label":"Properties","href":"/overcast/docs/getting-started/properties"},{"type":"link","label":"Libvirt","href":"/overcast/docs/getting-started/libvirt"},{"type":"link","label":"Docker","href":"/overcast/docs/getting-started/docker"},{"type":"link","label":"Examples","href":"/overcast/docs/getting-started/examples"},{"type":"link","label":"Development","href":"/overcast/docs/getting-started/development"}],"collapsed":true,"collapsible":true}]}}')}}]); -------------------------------------------------------------------------------- /docs/assets/js/9e4087bc.e0a9a0f2.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[608],{3012:function(e,t,a){a.r(t),a.d(t,{default:function(){return o}});var r=a(7294),n=a(6698),c=a(6742),l=a(4973);function i(e){var t=e.year,a=e.posts;return r.createElement(r.Fragment,null,r.createElement("h3",null,t),r.createElement("ul",null,a.map((function(e){return r.createElement("li",{key:e.metadata.date},r.createElement(c.Z,{to:e.metadata.permalink},e.metadata.formattedDate," - ",e.metadata.title))}))))}function m(e){var t=e.years;return r.createElement("section",{className:"margin-vert--lg"},r.createElement("div",{className:"container"},r.createElement("div",{className:"row"},t.map((function(e,t){return r.createElement("div",{key:t,className:"col col--4 margin-vert--lg"},r.createElement(i,e))})))))}function o(e){var t,a,c=e.archive,i=(0,l.I)({id:"theme.blog.archive.title",message:"Archive",description:"The page & hero title of the blog archive page"}),o=(0,l.I)({id:"theme.blog.archive.description",message:"Archive",description:"The page & hero description of the blog archive page"}),s=(t=c.blogPosts,a=t.reduceRight((function(e,t){var a=t.metadata.date.split("-")[0],r=e.get(a)||[];return e.set(a,[t].concat(r))}),new Map),Array.from(a,(function(e){return{year:e[0],posts:e[1]}})));return r.createElement(n.Z,{title:i,description:o},r.createElement("header",{className:"hero hero--primary"},r.createElement("div",{className:"container"},r.createElement("h1",{className:"hero__title"},i),r.createElement("p",{className:"hero__subtitle"},o))),r.createElement("main",null,s.length>0&&r.createElement(m,{years:s})))}}}]); -------------------------------------------------------------------------------- /docs/assets/js/a22865bc.9bd4aa3d.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[522],{8480:function(t){t.exports=JSON.parse('{"/overcast/blog/tags/overcast":{"allTagsPath":"/overcast/blog/tags","slug":"/overcast/blog/tags/overcast","name":"overcast","count":1,"permalink":"/overcast/blog/tags/overcast"},"/overcast/blog/tags/vmware":{"allTagsPath":"/overcast/blog/tags","slug":"/overcast/blog/tags/vmware","name":"vmware","count":1,"permalink":"/overcast/blog/tags/vmware"},"/overcast/blog/tags/integration-test":{"allTagsPath":"/overcast/blog/tags","slug":"/overcast/blog/tags/integration-test","name":"integration-test","count":1,"permalink":"/overcast/blog/tags/integration-test"}}')}}]); -------------------------------------------------------------------------------- /docs/assets/js/a2762952.6bf692cf.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[854],{8727:function(a){a.exports=JSON.parse('{"allTagsPath":"/overcast/blog/tags","slug":"/overcast/blog/tags/vmware","name":"vmware","count":1,"permalink":"/overcast/blog/tags/vmware"}')}}]); -------------------------------------------------------------------------------- /docs/assets/js/common.982779c9.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! ***************************************************************************** 2 | Copyright (c) Microsoft Corporation. 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 12 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | PERFORMANCE OF THIS SOFTWARE. 14 | ***************************************************************************** */ 15 | -------------------------------------------------------------------------------- /docs/assets/js/main.6f20bb18.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /* 2 | object-assign 3 | (c) Sindre Sorhus 4 | @license MIT 5 | */ 6 | 7 | /* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress 8 | * @license MIT */ 9 | 10 | /** @license React v0.20.2 11 | * scheduler.production.min.js 12 | * 13 | * Copyright (c) Facebook, Inc. and its affiliates. 14 | * 15 | * This source code is licensed under the MIT license found in the 16 | * LICENSE file in the root directory of this source tree. 17 | */ 18 | 19 | /** @license React v16.13.1 20 | * react-is.production.min.js 21 | * 22 | * Copyright (c) Facebook, Inc. and its affiliates. 23 | * 24 | * This source code is licensed under the MIT license found in the 25 | * LICENSE file in the root directory of this source tree. 26 | */ 27 | 28 | /** @license React v17.0.2 29 | * react-dom.production.min.js 30 | * 31 | * Copyright (c) Facebook, Inc. and its affiliates. 32 | * 33 | * This source code is licensed under the MIT license found in the 34 | * LICENSE file in the root directory of this source tree. 35 | */ 36 | 37 | /** @license React v17.0.2 38 | * react.production.min.js 39 | * 40 | * Copyright (c) Facebook, Inc. and its affiliates. 41 | * 42 | * This source code is licensed under the MIT license found in the 43 | * LICENSE file in the root directory of this source tree. 44 | */ 45 | -------------------------------------------------------------------------------- /docs/assets/js/runtime~main.48a448b1.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";var e,t,n,r,o,a={},f={};function c(e){var t=f[e];if(void 0!==t)return t.exports;var n=f[e]={id:e,loaded:!1,exports:{}};return a[e].call(n.exports,n,n.exports,c),n.loaded=!0,n.exports}c.m=a,c.c=f,e=[],c.O=function(t,n,r,o){if(!n){var a=1/0;for(d=0;d=o)&&Object.keys(c.O).every((function(e){return c.O[e](n[u])}))?n.splice(u--,1):(f=!1,o0&&e[d-1][2]>o;d--)e[d]=e[d-1];e[d]=[n,r,o]},c.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return c.d(t,{a:t}),t},n=Object.getPrototypeOf?function(e){return Object.getPrototypeOf(e)}:function(e){return e.__proto__},c.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var o=Object.create(null);c.r(o);var a={};t=t||[null,n({}),n([]),n(n)];for(var f=2&r&&e;"object"==typeof f&&!~t.indexOf(f);f=n(f))Object.getOwnPropertyNames(f).forEach((function(t){a[t]=function(){return e[t]}}));return a.default=function(){return e},c.d(o,a),o},c.d=function(e,t){for(var n in t)c.o(t,n)&&!c.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},c.f={},c.e=function(e){return Promise.all(Object.keys(c.f).reduce((function(t,n){return c.f[n](e,t),t}),[]))},c.u=function(e){return"assets/js/"+({5:"65664f72",13:"01a85c17",19:"9945d209",39:"1d48e3ab",53:"935f2afb",89:"a6aa9e1f",90:"508a7d2f",103:"ccc49370",121:"55960ee5",127:"15bd92de",152:"54f44165",177:"34a804db",195:"c4f5d8e4",240:"0aa62002",241:"8965b357",380:"763fcd15",490:"b3ff6462",514:"1be78505",522:"a22865bc",523:"ae0f9d55",535:"814f3328",592:"common",608:"9e4087bc",610:"6875c492",671:"0e384e19",751:"3720c009",761:"74c14969",854:"a2762952",918:"17896441"}[e]||e)+"."+{5:"2e7a62fc",13:"201a70b5",19:"f9563fd6",39:"8ff3c9fc",53:"11666b9e",75:"8fe7757f",89:"04e0c149",90:"43c89018",103:"8f94f054",121:"72573837",127:"6623578a",152:"5cf5c70a",177:"93cce336",195:"3c135bce",240:"6e47bd2d",241:"5ced651d",380:"95a8f898",490:"3df797cc",514:"ef0ee4d7",522:"9bd4aa3d",523:"f107f8ab",535:"db8f94e4",592:"982779c9",608:"e0a9a0f2",610:"2566d241",671:"61658283",751:"46d24c30",761:"f1b0f9db",845:"9544dcf2",854:"6bf692cf",918:"c15c396c"}[e]+".js"},c.miniCssF=function(e){return"assets/css/styles.fb53c356.css"},c.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),c.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r={},o="documentation:",c.l=function(e,t,n,a){if(r[e])r[e].push(t);else{var f,u;if(void 0!==n)for(var i=document.getElementsByTagName("script"),d=0;d 2 | 3 | https://xebialabs.github.io/overcast/blog 4 | Overcast Blog 5 | 2021-09-15T00:00:00.000Z 6 | https://github.com/jpmonette/feed 7 | 8 | Overcast Blog 9 | https://xebialabs.github.io/overcast/img/digital_ai_deploy.svg 10 | 11 | <![CDATA[How to run integration tests against VM Ware]]> 12 | How to run integration tests against VM Ware 13 | 14 | 2021-09-15T00:00:00.000Z 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/blog/rss.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Overcast Blog 5 | https://xebialabs.github.io/overcast/blog 6 | Overcast Blog 7 | Wed, 15 Sep 2021 00:00:00 GMT 8 | https://validator.w3.org/feed/docs/rss2.html 9 | https://github.com/jpmonette/feed 10 | 11 | <![CDATA[How to run integration tests against VM Ware]]> 12 | https://xebialabs.github.io/overcast/blog/2021/09/15/how-to-run-integration-tests-against-vmware 13 | How to run integration tests against VM Ware 14 | Wed, 15 Sep 2021 00:00:00 GMT 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /docs/docs/tags/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Tags | Overcast 9 | 10 | 11 | 12 | 13 |
14 |

Tags

15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /docs/img/1.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /docs/img/2.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /docs/img/3.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /docs/sitemap.xml: -------------------------------------------------------------------------------- 1 | https://xebialabs.github.io/overcast/weekly0.5https://xebialabs.github.io/overcast/blogweekly0.5https://xebialabs.github.io/overcast/blog/2021/09/15/how-to-run-integration-tests-against-vmwareweekly0.5https://xebialabs.github.io/overcast/blog/archiveweekly0.5https://xebialabs.github.io/overcast/blog/tagsweekly0.5https://xebialabs.github.io/overcast/blog/tags/integration-testweekly0.5https://xebialabs.github.io/overcast/blog/tags/overcastweekly0.5https://xebialabs.github.io/overcast/blog/tags/vmwareweekly0.5https://xebialabs.github.io/overcast/docs/tagsweekly0.5https://xebialabs.github.io/overcast/docs/getting-started/developmentweekly0.5https://xebialabs.github.io/overcast/docs/getting-started/dockerweekly0.5https://xebialabs.github.io/overcast/docs/getting-started/examplesweekly0.5https://xebialabs.github.io/overcast/docs/getting-started/featuresweekly0.5https://xebialabs.github.io/overcast/docs/getting-started/installationweekly0.5https://xebialabs.github.io/overcast/docs/getting-started/libvirtweekly0.5https://xebialabs.github.io/overcast/docs/getting-started/propertiesweekly0.5https://xebialabs.github.io/overcast/docs/introweekly0.5 -------------------------------------------------------------------------------- /documentation/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /documentation/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /documentation/blog/2021-09-15-how-to-run-integration-tests-against-vmware.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: How to run integration tests against VM Ware 3 | tags: 4 | - overcast 5 | - vmware 6 | - integration-test 7 | --- 8 | 9 | This article is for you, if you have to run VMs for your integration tests, and you use as a hypervisor - VM Ware 7.0 10 | or above. 11 | 12 | The flow of each test case is the following: 13 | 14 | ![Test case flow](./pics/vmware/vmware-test-case-flow.jpg) 15 | 16 | The participation of this library is for 2 blocks, to set up VM and to tear it down. 17 | In order to set up VM you have to choose which one you need. 18 | For that you are preparing it in your VMWare, like for example, `base-ubuntu20` and during the test run it will be cloned. 19 | The image is cloned so that it can be reused multiple times, and you can run parallel tests. 20 | 21 | There are two ways to clone the VM, **clone** and **instant clone**:
22 | * **Clone** you can do on Powered off base images.
23 | * **Instant clone** works only on images which are Powered on, because it happens on the memory level, 24 | and the clone process significantly faster. 25 | 26 | The operational process is happening via REST API. 27 | 28 | * First we open the session, and for that we have to know the API host of the VMWare and credentials. Credentials 29 | are provided as a base 64 encrypted hash of `username:password`. Once you have it you define it in your `overcast.conf` like: 30 | 31 | ```shell script title=overcast.conf 32 | overcastVMWareHost { # this is an arbitrary name which will be used in your test to read the configuration section. 33 | ... 34 | vmwareApiHost=my_api_host 35 | vmwareAuthHashCredentials=my_hash_credentials 36 | ... 37 | } 38 | ``` 39 | 40 | * Then by VM name we have to find what is VM id is. To define the base image in overcast you have to add this property: 41 | 42 | ```shell script title=overcast.conf 43 | overcastVMWareHost { 44 | ... 45 | vmBaseImage=my_base_image_name 46 | ... 47 | } 48 | ``` 49 | 50 | * Having `sessionId` and `vmId` is enough to start the cloning. Though you can choose between the type of cloning. By 51 | default, it is **instant clone**, if by some reason you want to use a usual clone, you can add this line to the configuration: 52 | 53 | ```shell script title=overcast.conf 54 | overcastVMWareHost { 55 | ... 56 | instantClone=false 57 | ... 58 | } 59 | ``` 60 | 61 | In my case, I got 120 seconds to clone CentOs 7, and 3 seconds to instantly clone the same image. 62 | The difference is phenomenal. But take into account that the base image has to be powered on for the instant clone. 63 | 64 | * Then you are provisioning your VM, if it is required. You can for example use [Overthere](https://github.com/xebialabs/overthere) 65 | for that. Or you can skip it and have already base image provisioned for your testing needs. 66 | 67 | * After it's a time to execute the test logic itself. 68 | 69 | * Once all is done, as the best practice, we have to utilise all our resources. For that we are shutting down the VM and 70 | deleting it. 71 | 72 | The snippet of the code which you can use as a scaffold for that is: 73 | 74 | ```java 75 | @Test 76 | public void shouldCloneAndDisposeVm() { 77 | VMWareHost h = (VMWareHost) CloudHostFactory.getCloudHost("overcastVMWareHost"); 78 | try { 79 | h.setup(); 80 | 81 | assertThat(h.getSessionId().length(), is(32)); 82 | assertThat(h.getHostName(), notNullValue()); 83 | } catch (Exception e) { 84 | fail(e.getMessage()); 85 | } finally { 86 | h.teardown(); 87 | } 88 | } 89 | ``` 90 | 91 | 92 | As we can see from here in this line
93 | ```java 94 | VMWareHost h = (VMWareHost) CloudHostFactory.getCloudHost("overcastVMWareHost"); 95 | ``` 96 | 97 |
we 98 | are reading from the defined section `overcastVMWareHost`. The casting is not necessary here.
99 | It is only to verify that session was properly created. As it is specified for VMWare implementation. 100 | 101 | After `h.setup()` you can do provisioning and writing your test scenario. 102 | -------------------------------------------------------------------------------- /documentation/blog/pics/vmware/vmware-test-case-flow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xebialabs/overcast/8a494dc8f8572ae337ce91b06829eb9567c91f5d/documentation/blog/pics/vmware/vmware-test-case-flow.jpg -------------------------------------------------------------------------------- /documentation/docs/getting-started/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Getting Started", 3 | "position": 2 4 | } 5 | -------------------------------------------------------------------------------- /documentation/docs/getting-started/development.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 7 3 | --- 4 | 5 | # Development 6 | 7 | ## How to build the project 8 | 9 | `./gradlew clean build` 10 | 11 | ## Where documentation resides 12 | 13 | You can find the documentation to edit in documentation/docs folder. The `docs` folder contains built documentation 14 | which is served on GitHub Pages. 15 | 16 | ## How to run documentation site locally 17 | 18 | `./gradlew yarnRunStart` 19 | 20 | The site will be opened automatically in your default browser on page: [http://localhost:3001/overcast/](http://localhost:3001/overcast/) 21 | 22 | ## How to generate the documentation for GitHub 23 | 24 | `./gradlew docBuild` and commit all modified files in docs folder. 25 | -------------------------------------------------------------------------------- /documentation/docs/getting-started/docker.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Docker 6 | 7 | ## Docker concepts 8 | During `setup()`, Overcast will create and start a new Docker container. If the image specified is not available in the local registry, it will be automatically pulled from the central Docker repository. 9 | 10 | During `teardown()`, it will stop the container and optionally remove the container (see remove property). 11 | 12 | Calling `getHostName()` will return the hostname of the Docker Host, assuming the container will run on that host, with the exposed ports accessible on the Docker host. 13 | 14 | Calling `getPort(port)` will translate the internal port (passed as an argument) to the port externally exposed by the Docker Container. The port number is dynamically determined by Docker. The port range used for dynamic allocation is 49153 to 65535 (defined by Docker). 15 | 16 | We use the [Spotify Docker Client](https://github.com/spotify/docker-client) library. 17 | 18 | -------------------------------------------------------------------------------- /documentation/docs/getting-started/examples.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # Examples 6 | 7 | ## Set up and Tear down 8 | 9 | @BeforeClass 10 | public static void doInitHost() { 11 | CloudHostFactory.getCloudHost("{my-host-label}").setup(); 12 | } 13 | 14 | @AfterClass 15 | public static void doTeardownHost() { 16 | CloudHostFactory.getCloudHost("{my-host-label}").teardown(); 17 | } 18 | 19 | Also Overcast is used for integration tests of [Overthere](https://github.com/xebialabs/overthere). 20 | 21 | -------------------------------------------------------------------------------- /documentation/docs/getting-started/features.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Features 6 | 7 | * Decouple test and test machine setup. 8 | * Setup and tear-down for 9 | - Amazon EC2 hosts (Automatic host creation/destroy) 10 | - Vagrant hosts (Set up to the running state, tear down to the initial state) 11 | - VirtualBox hosts (Load snapshot and start, power off) 12 | - Libvirt managed KVM hosts (Fast clones using backing store, provisioning) 13 | - Docker containers 14 | - Tunneled cloud hosts (Reaching target host via ssh tunnel) 15 | - VMWare hosts (Instant clones, start/destroy VMs) 16 | 17 | * Provides hostname and port mapping of created host (@see Ec2CloudHost) 18 | * Caching of provisioned hosts (vagrant and KVM) with expiration checks 19 | -------------------------------------------------------------------------------- /documentation/docs/getting-started/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Installation 6 | 7 | ## Requirements 8 | 9 | - Virtualbox version >= 4.2 10 | - Vagrant version >= 1.2.7 11 | - Qemu/KVM version that supports domain metadata (QEMU-KVM 1.4.2 (Fedora 19), 2.0.0 (Ubuntu LTS 14)) 12 | - Docker >= 1.6 13 | - VMWare >= 7.0 14 | 15 | ## From maven repo 16 | 17 | [http://mvnrepository.com/artifact/com.xebialabs.cloud/overcast/2.5.1](http://mvnrepository.com/artifact/com.xebialabs.cloud/overcast/2.5.1) 18 | 19 | 20 | com.xebialabs.cloud 21 | overcast 22 | 2.5.1 23 | 24 | 25 | Note: the libvirt JNA wrapper may require adding the libvirt.org repository to your build: [http://www.libvirt.org/maven2/](http://www.libvirt.org/maven2/) 26 | 27 | ## From sources 28 | 29 | ```gradle build``` 30 | 31 | ## Usage 32 | 33 | ### Set up your host 34 | Overcast looks for configuration properties in this order: 35 | 36 | * `System.getProperty()` 37 | * `/.overcast/overcast.conf` 38 | * `/overcast.conf` 39 | * `/overcast.conf` 40 | 41 | :::note 42 | The home location takes precedence over the project location.
This allows developers to adapt settings to their local setup without changing the project defaults. 43 | ::: 44 | 45 | The `overcast.conf` files are in [Typesafe Config HOCON syntax](https://github.com/typesafehub/config#using-hocon-the-json-superset).
This is a flexible JSON superset that allows comments, substitution, file inclusion, and more. 46 | -------------------------------------------------------------------------------- /documentation/docs/getting-started/libvirt.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Libvirt 6 | 7 | The libvirt implementation uses backing store images. This means that the domain being cloned needs to be shut down. When cloning a system all disks of the base system are cloned using a backing store, and thrown away upon teardown, thus leaving the original system unchanged. 8 | 9 | Machines can use static IP's using `{host}.ipLookupStrategy=static`. It is up to you that you do not 10 | start more than one. It is also possible to use DHCP using `{host}.ipLookupStrategy=SSH`. 11 | You have to specify the name of the Virtual Network in libvirt or the name of the bridge the domain 12 | is connected to and a command to lookup the IP on the DHCP server giving the system it's IP address. 13 | The IP can then be retrieved using the ```getHostName()``` method on the ```CloudHost```. 14 | 15 | ## NAT network 16 | Due to the way NAT works the machine would only be accessible from the Libvirt (KVM) host. Example settings for a (NAT) network named `my_nat_network`: 17 | 18 | nat_host { 19 | libvirtURL="qemu+ssh://user@linux-box/system" 20 | libvirtBaseDomain="my_base_domain" 21 | networkDeviceId="my_nat_network" 22 | ipLookupStrategy="SSH" 23 | SSH { 24 | url="ssh://user@linux-box?os=UNIX&connectionType=SCP&privateKeyFile="${user.home}"/.ssh/id_rsa&passphrase=bigsecret" 25 | command="""grep {0} /var/lib/libvirt/dnsmasq/my_nat_network.leases | cut -d " " -f 3""" 26 | timeout=30 27 | } 28 | } 29 | 30 | ## Bridged network 31 | Example settings for a host connected to a bridge named `br0`. Assuming a DHCP server `dhcp-box`: 32 | 33 | bridged_host { 34 | libvirtURL="qemu+ssh://user@linux-box/system" 35 | libvirtBaseDomain="my_base_domain" 36 | networkDeviceId="br0" 37 | ipLookupStrategy="SSH" 38 | SSH { 39 | url="ssh://dhcp-query-user@dhcp-box?os=UNIX&connectionType=SCP&privateKeyFile="${user.home}"/.ssh/id_rsa&passphrase=bigsecret" 40 | command="""grep {0} /var/lib/dnsmasq/dnsmasq.leases | cut -d " " -f 3""" 41 | timeout=30 42 | } 43 | } 44 | 45 | ## Routed network 46 | 47 | A routed network is similar to a bridged network with the difference that `networkDeviceId` should be set to the name of the routed network. 48 | 49 | ## File system mapping 50 | 51 | With entries in the `{host}.fsMapping` section it is possible to mount directories from the host in the created domain. For instance like: 52 | 53 | my-host { 54 | ... 55 | fsMapping { 56 | vagrant { hostPath = ${itest.vagrantDir}"/itest/vagrant", readOnly = true } 57 | data { hostPath = ${itest.dataDir}, readOnly = true, accessMode = "mapped" } 58 | } 59 | } 60 | 61 | These mappings can be mounted on the domain with fstab entries like: 62 | 63 | data /data 9p ro,trans=virtio 64 | vagrant /vagrant 9p ro,trans=virtio 65 | 66 | For details on the accessMode variants see: [http://libvirt.org/formatdomain.html#elementsFilesystems](http://libvirt.org/formatdomain.html#elementsFilesystems). 67 | 68 | It may be necessary to add the 9p file system drivers to the initrd image of the base domain image. 69 | -------------------------------------------------------------------------------- /documentation/docs/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Introduction 6 | 7 | A Java library to test against hosts in the cloud. 8 | -------------------------------------------------------------------------------- /documentation/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | const lightCodeTheme = require('prism-react-renderer/themes/github'); 2 | const darkCodeTheme = require('prism-react-renderer/themes/dracula'); 3 | 4 | module.exports = { 5 | title: 'Overcast', 6 | tagline: 'Cloud test -- Java helper classes to write your tests against hosts in the cloud', 7 | url: 'https://xebialabs.github.io', 8 | baseUrl: '/overcast/', 9 | onBrokenLinks: 'throw', 10 | onBrokenMarkdownLinks: 'warn', 11 | favicon: 'img/digital_ai_deploy.svg', 12 | organizationName: 'Digital.ai', 13 | projectName: 'overcast', 14 | themeConfig: { 15 | navbar: { 16 | title: 'Overcast', 17 | logo: { 18 | alt: 'Overcast Digital.ai', 19 | src: 'img/digital_ai_deploy.svg', 20 | }, 21 | items: [ 22 | { 23 | type: 'doc', 24 | docId: 'intro', 25 | position: 'left', 26 | label: 'Tutorial', 27 | }, 28 | 29 | { 30 | href: 'https://github.com/xebialabs/overcast', 31 | label: 'GitHub', 32 | position: 'right', 33 | }, 34 | { 35 | to: 'blog', 36 | label: 'Blog', 37 | position: 'left' 38 | } 39 | ], 40 | }, 41 | footer: { 42 | style: 'dark', 43 | links: [ 44 | { 45 | title: 'Docs', 46 | items: [ 47 | { 48 | label: 'Tutorial', 49 | to: '/docs/intro', 50 | }, 51 | { 52 | label: 'Blog', 53 | to: '/blog', 54 | }, 55 | { 56 | label: 'GitHub', 57 | href: 'https://github.com/xebialabs/overcast', 58 | }, 59 | ], 60 | }, 61 | ], 62 | copyright: `Copyright © ${new Date().getFullYear()} Overcast Digital.ai`, 63 | }, 64 | prism: { 65 | theme: lightCodeTheme, 66 | darkTheme: darkCodeTheme, 67 | }, 68 | }, 69 | presets: [ 70 | [ 71 | '@docusaurus/preset-classic', 72 | { 73 | docs: { 74 | sidebarPath: require.resolve('./sidebars.js') 75 | }, 76 | blog: { 77 | showReadingTime: true 78 | }, 79 | theme: { 80 | customCss: require.resolve('./src/css/custom.css'), 81 | }, 82 | }, 83 | ], 84 | ], 85 | }; 86 | -------------------------------------------------------------------------------- /documentation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "documentation", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start --port=3001", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "2.0.0-beta.6", 18 | "@docusaurus/preset-classic": "2.0.0-beta.6", 19 | "@mdx-js/react": "^1.6.21", 20 | "@svgr/webpack": "^5.5.0", 21 | "clsx": "^1.1.1", 22 | "file-loader": "^6.2.0", 23 | "prism-react-renderer": "^1.2.1", 24 | "react": "^17.0.1", 25 | "react-dom": "^17.0.1", 26 | "url-loader": "^4.1.1" 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.5%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /documentation/sidebars.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}] 3 | }; 4 | -------------------------------------------------------------------------------- /documentation/src/components/HomepageFeatures.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import styles from './HomepageFeatures.module.css'; 4 | 5 | const FeatureList = [ 6 | { 7 | title: 'Easy to Use', 8 | Svg: require('../../static/img/1.svg').default, 9 | description: ( 10 | <> 11 | Overcast was designed from the ground up to be easy in use to spin up 12 | virtual machines for integration testing. 13 | 14 | ), 15 | }, 16 | { 17 | title: 'Focus on What Matters', 18 | Svg: require('../../static/img/2.svg').default, 19 | description: ( 20 | <> 21 | Overcast lets you focus on your test scenarios, and we'll do the chores to setup the environment. 22 | Go ahead and try it out. 23 | 24 | ), 25 | }, 26 | { 27 | title: 'Backed by Digital.ai', 28 | Svg: require('../../static/img/3.svg').default, 29 | description: ( 30 | <> 31 | Overcast made our test environment easier to operate with. 32 | 33 | ), 34 | }, 35 | ]; 36 | 37 | function Feature({Svg, title, description}) { 38 | return ( 39 |
40 |
41 | 42 |
43 |
44 |

{title}

45 |

{description}

46 |
47 |
48 | ); 49 | } 50 | 51 | export default function HomepageFeatures() { 52 | return ( 53 |
54 |
55 |
56 | {FeatureList.map((props, idx) => ( 57 | 58 | ))} 59 |
60 |
61 |
62 | ); 63 | } 64 | -------------------------------------------------------------------------------- /documentation/src/components/HomepageFeatures.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /documentation/src/css/custom.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --ifm-color-primary: #498500; 3 | --ifm-color-primary-dark: rgb(33, 175, 144); 4 | --ifm-color-primary-darker: rgb(31, 165, 136); 5 | --ifm-color-primary-darkest: rgb(26, 136, 112); 6 | --ifm-color-primary-light: rgb(70, 203, 174); 7 | --ifm-color-primary-lighter: rgb(102, 212, 189); 8 | --ifm-color-primary-lightest: rgb(146, 224, 208); 9 | --ifm-code-font-size: 95%; 10 | } 11 | 12 | .digitalai-highlight-code-line { 13 | background-color: rgba(0, 0, 0, 0.1); 14 | display: block; 15 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 16 | padding: 0 var(--ifm-pre-padding); 17 | } 18 | 19 | html[data-theme='dark'] .digitalai-highlight-code-line { 20 | background-color: rgba(0, 0, 0, 0.3); 21 | } 22 | 23 | .main-wrapper .container svg { 24 | height: 130px; 25 | width: 130px; 26 | } 27 | 28 | .navbar, .navbar * { 29 | background-color: white; 30 | color: black; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /documentation/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import Layout from '@theme/Layout'; 4 | import Link from '@docusaurus/Link'; 5 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 6 | import styles from './index.module.css'; 7 | import HomepageFeatures from '../components/HomepageFeatures'; 8 | 9 | function HomepageHeader() { 10 | const {siteConfig} = useDocusaurusContext(); 11 | return ( 12 |
13 |
14 |

{siteConfig.title}

15 |

{siteConfig.tagline}

16 |
17 | 20 | Overcast Tutorial 21 | 22 |
23 |
24 |
25 | ); 26 | } 27 | 28 | export default function Home() { 29 | const {siteConfig} = useDocusaurusContext(); 30 | return ( 31 | 34 | 35 |
36 | 37 |
38 |
39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /documentation/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | .heroBanner { 2 | padding: 4rem 0; 3 | text-align: center; 4 | position: relative; 5 | overflow: hidden; 6 | } 7 | 8 | @media screen and (max-width: 966px) { 9 | .heroBanner { 10 | padding: 2rem; 11 | } 12 | } 13 | 14 | .buttons { 15 | display: flex; 16 | align-items: center; 17 | justify-content: center; 18 | } 19 | -------------------------------------------------------------------------------- /documentation/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xebialabs/overcast/8a494dc8f8572ae337ce91b06829eb9567c91f5d/documentation/static/.nojekyll -------------------------------------------------------------------------------- /documentation/static/img/1.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /documentation/static/img/2.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /documentation/static/img/3.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | hamcrestVersion=1.3 2 | jUnitVersion=5.6.2 3 | languageLevel=11 4 | logbackVersion=1.0.9 5 | mockitoCoreVersion=2.25.1 6 | slf4jVersion=1.7.16 7 | gradleCommitPluginVersion=1.0.0-930.1747 8 | -------------------------------------------------------------------------------- /gradle/license-header: -------------------------------------------------------------------------------- 1 | Copyright 2012-${year} ${name} 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xebialabs/overcast/8a494dc8f8572ae337ce91b06829eb9567c91f5d/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jan 15 10:38:41 CET 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /itest/overcast-docker-itest/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | testImplementation project(":") 3 | 4 | testImplementation "com.spotify:docker-client:8.16.0" 5 | 6 | testImplementation "org.hamcrest:hamcrest-core:${hamcrestVersion}" 7 | testImplementation "org.hamcrest:hamcrest-library:${hamcrestVersion}" 8 | testImplementation "org.junit.jupiter:junit-jupiter-api:${jUnitVersion}" 9 | 10 | testImplementation("org.mockito:mockito-core:${mockitoCoreVersion}") { 11 | exclude group: 'org.hamcrest', module: 'hamcrest-core' 12 | } 13 | 14 | testRuntimeOnly "ch.qos.logback:logback-classic:${logbackVersion}" 15 | testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${jUnitVersion}" 16 | testRuntimeOnly "org.slf4j:jcl-over-slf4j:${slf4jVersion}" 17 | testRuntimeOnly "org.slf4j:log4j-over-slf4j:${slf4jVersion}" 18 | } 19 | 20 | -------------------------------------------------------------------------------- /itest/overcast-docker-itest/src/test/resources/overcast.conf: -------------------------------------------------------------------------------- 1 | 2 | // Configuration needed in users overcast.conf 3 | // overcastItest { 4 | // dockerHost=http://localhost:2375 5 | // } 6 | 7 | dockerMinimalConfig { 8 | dockerImage="busybox:1" 9 | command=["/bin/sh", "-c", "while true; do echo hello world; sleep 1; done"] 10 | remove=true 11 | removeVolume=true 12 | } 13 | 14 | greeterConfig { 15 | dockerHost="unix:///var/run/docker.sock" 16 | dockerHost=${?overcastItest.dockerHost} 17 | certificates=${?overcastItest.dockerCertificates} 18 | dockerImage="busybox:1" 19 | name="greeter" 20 | command=["/bin/sh", "-c", "echo hi | nc -l -p 8080"] 21 | remove=true 22 | removeVolume=true 23 | } 24 | 25 | dockerLinksConfig { 26 | dockerHost="unix:///var/run/docker.sock" 27 | dockerHost=${?overcastItest.dockerHost} 28 | certificates=${?overcastItest.dockerCertificates} 29 | dockerImage="busybox:1" 30 | name="receiver" 31 | links=["greeter:greeter"] 32 | command=["/bin/sh", "-c", "while ! nc greeter 8080; do sleep 1; done"] 33 | remove=true 34 | removeVolume=true 35 | } 36 | 37 | dockerAdvancedConfig { 38 | dockerHost="unix:///var/run/docker.sock" 39 | dockerHost=${?overcastItest.dockerHost} 40 | certificates=${?overcastItest.dockerCertificates} 41 | dockerImage="busybox:1" 42 | name="mycontainer" 43 | exposedPorts=["12345/tcp", "23456/tcp", "34567/tcp"] 44 | exposeAllPorts=true 45 | command=["/bin/sh", "-c", "while true; do echo hello world; sleep 1; done"] 46 | env = ["MYVAR1=AAA", "MYVAR2=BBB", "MYVAR3=CCC"] 47 | remove=true 48 | removeVolume=true 49 | } 50 | 51 | dockerAdvancedConfigTty { 52 | dockerHost="unix:///var/run/docker.sock" 53 | dockerHost=${?overcastItest.dockerHost} 54 | certificates=${?overcastItest.dockerCertificates} 55 | dockerImage="busybox:1" 56 | name="mycontainer" 57 | exposedPorts=["12345/tcp", "23456/tcp", "34567/tcp"] 58 | exposeAllPorts=true 59 | command=["/bin/sh", "-c", "while true; do echo hello world; sleep 1; done"] 60 | env = ["MYVAR1=AAA", "MYVAR2=BBB", "MYVAR3=CCC"] 61 | tty=true 62 | remove=true 63 | removeVolume=true 64 | } 65 | -------------------------------------------------------------------------------- /itest/overcast-libvirt-itest/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | testImplementation project(":") 3 | 4 | testImplementation "org.hamcrest:hamcrest-core:${hamcrestVersion}" 5 | testImplementation "org.hamcrest:hamcrest-library:${hamcrestVersion}" 6 | testImplementation 'org.jdom:jdom2:2.0.5' 7 | testImplementation "org.junit.jupiter:junit-jupiter-api:${jUnitVersion}" 8 | testImplementation 'org.libvirt:libvirt:0.5.1' 9 | testImplementation "org.slf4j:slf4j-api:${slf4jVersion}" 10 | testImplementation("org.mockito:mockito-core:${mockitoCoreVersion}") { 11 | exclude group: 'org.hamcrest', module: 'hamcrest-core' 12 | } 13 | 14 | testRuntimeOnly "ch.qos.logback:logback-classic:${logbackVersion}" 15 | testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${jUnitVersion}" 16 | testRuntimeOnly "org.slf4j:jcl-over-slf4j:${slf4jVersion}" 17 | testRuntimeOnly "org.slf4j:log4j-over-slf4j:${slf4jVersion}" 18 | } 19 | 20 | -------------------------------------------------------------------------------- /itest/overcast-vagrant-itest/box/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure("2") do |config| 5 | 6 | config.vm.provider :virtualbox do |vb| 7 | vb.customize ["modifyvm", :id, "--name", "overcast_itest"] 8 | end 9 | 10 | config.vm.box = "ubuntu/trusty64" 11 | 12 | config.vm.define "overcast_itest" do |oi| 13 | oi.vm.network :private_network, ip: "10.10.200.200" 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /itest/overcast-vagrant-itest/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | testImplementation project(":") 3 | 4 | testImplementation "org.hamcrest:hamcrest-core:${hamcrestVersion}" 5 | testImplementation "org.hamcrest:hamcrest-library:${hamcrestVersion}" 6 | testImplementation "org.junit.jupiter:junit-jupiter-api:${jUnitVersion}" 7 | testImplementation("org.mockito:mockito-core:${mockitoCoreVersion}") 8 | 9 | testRuntimeOnly "ch.qos.logback:logback-classic:${logbackVersion}" 10 | testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${jUnitVersion}" 11 | testRuntimeOnly "org.slf4j:jcl-over-slf4j:${slf4jVersion}" 12 | testRuntimeOnly "org.slf4j:log4j-over-slf4j:${slf4jVersion}" 13 | } 14 | 15 | test.doFirst { 16 | println properties 17 | } 18 | -------------------------------------------------------------------------------- /itest/overcast-vagrant-itest/src/test/java/com/xebialabs/overcast/host/CachedVagrantCloudHostItest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.host; 17 | 18 | import com.xebialabs.overcast.command.CommandProcessor; 19 | import com.xebialabs.overcast.support.vagrant.VagrantDriver; 20 | import com.xebialabs.overcast.support.virtualbox.VirtualboxDriver; 21 | import com.xebialabs.overcast.support.virtualbox.VirtualboxState; 22 | import org.junit.jupiter.api.AfterEach; 23 | import org.junit.jupiter.api.BeforeEach; 24 | import org.junit.jupiter.api.Test; 25 | 26 | import static org.hamcrest.CoreMatchers.is; 27 | import static org.hamcrest.MatcherAssert.assertThat; 28 | 29 | public class CachedVagrantCloudHostItest { 30 | 31 | public static final String VM = "overcast_itest"; 32 | private VirtualboxDriver vbox; 33 | private VagrantDriver vagrant; 34 | private CommandProcessor cmd; 35 | 36 | @BeforeEach 37 | public void before() { 38 | cmd = CommandProcessor.atLocation("./box"); 39 | vagrant = new VagrantDriver(VM, cmd); 40 | vbox = new VirtualboxDriver(cmd); 41 | } 42 | 43 | @AfterEach 44 | public void after() { 45 | vagrant.doVagrant(VM, "destroy", "-f"); 46 | } 47 | 48 | @Test 49 | public void shouldSetUpAndTearDownTheVm() { 50 | CloudHost h = CloudHostFactory.getCloudHost("overcastVagrantHost"); 51 | h.setup(); 52 | 53 | assertThat(vbox.vmExists(VM), is(true)); 54 | assertThat(vbox.vmState(VM), is(VirtualboxState.RUNNING)); 55 | 56 | h.teardown(); 57 | 58 | assertThat(vbox.vmState(VM), is(VirtualboxState.POWEROFF)); 59 | assertThat(vbox.getExtraData(VM, CachedVagrantCloudHost.EXPIRATION_TAG_PROPERTY_KEY), is("'ohai'")); 60 | 61 | h.setup(); 62 | 63 | assertThat(vbox.vmState(VM), is(VirtualboxState.RUNNING)); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /itest/overcast-vagrant-itest/src/test/resources/overcast.conf: -------------------------------------------------------------------------------- 1 | overcastVagrantHost { 2 | vagrantDir="./box" 3 | vagrantIp="10.10.200.200" 4 | vagrantVm="overcast_itest" 5 | vagrantSnapshotExpirationCmd="echo 'ohai'" 6 | } 7 | -------------------------------------------------------------------------------- /itest/overcast-vmware-itest/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | testImplementation project(":") 3 | 4 | testImplementation "org.hamcrest:hamcrest-core:${hamcrestVersion}" 5 | testImplementation "org.hamcrest:hamcrest-library:${hamcrestVersion}" 6 | testImplementation "org.junit.jupiter:junit-jupiter-api:${jUnitVersion}" 7 | testImplementation("org.mockito:mockito-core:${mockitoCoreVersion}") 8 | 9 | testRuntimeOnly "ch.qos.logback:logback-classic:${logbackVersion}" 10 | testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${jUnitVersion}" 11 | testRuntimeOnly "org.slf4j:jcl-over-slf4j:${slf4jVersion}" 12 | testRuntimeOnly "org.slf4j:log4j-over-slf4j:${slf4jVersion}" 13 | } 14 | 15 | test.doFirst { 16 | println properties 17 | } 18 | -------------------------------------------------------------------------------- /itest/overcast-vmware-itest/src/test/java/com/xebialabs/overcast/host/VMWareCloudHostItest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.host; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import static org.hamcrest.CoreMatchers.is; 21 | import static org.hamcrest.CoreMatchers.notNullValue; 22 | import static org.hamcrest.MatcherAssert.assertThat; 23 | import static org.junit.jupiter.api.Assertions.fail; 24 | 25 | public class VMWareCloudHostItest { 26 | 27 | @Test 28 | public void shouldCloneAndDisposeVm() { 29 | VMWareHost h = (VMWareHost) CloudHostFactory.getCloudHost("overcastVMWareHost"); 30 | try { 31 | h.setup(); 32 | 33 | assertThat(h.getSessionId().length(), is(32)); 34 | assertThat(h.getHostName(), notNullValue()); 35 | } catch (Exception e) { 36 | fail(e.getMessage()); 37 | } finally { 38 | h.teardown(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /itest/overcast-vmware-itest/src/test/resources/overcast.conf: -------------------------------------------------------------------------------- 1 | overcastVMWareHost { 2 | vmBaseImage=Base_centos7 3 | 4 | # vmwareApiHost=YOUR HOST HERE 5 | # vmwareAuthHashCredentials=YOUR AUTH HASH HERE 6 | } 7 | -------------------------------------------------------------------------------- /overcast.conf: -------------------------------------------------------------------------------- 1 | // used in unit test 2 | precedenceTestValue=valueFromWork 3 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenLocal() 4 | gradlePluginPortal() 5 | maven { 6 | url = uri("https://oss.sonatype.org/service/local/repositories/releases/content") 7 | } 8 | } 9 | } 10 | 11 | rootProject.name = 'overcast' 12 | 13 | include 'itest:overcast-docker-itest' 14 | include 'itest:overcast-libvirt-itest' 15 | include 'itest:overcast-vagrant-itest' 16 | include 'itest:overcast-vmware-itest' 17 | 18 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/OverthereUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast; 17 | 18 | import com.xebialabs.overthere.ConnectionOptions; 19 | import com.xebialabs.overthere.Overthere; 20 | import com.xebialabs.overthere.OverthereConnection; 21 | import com.xebialabs.overthere.OverthereFile; 22 | import org.apache.http.NameValuePair; 23 | import org.apache.http.client.utils.URLEncodedUtils; 24 | 25 | import java.net.URI; 26 | import java.net.URISyntaxException; 27 | import java.nio.charset.StandardCharsets; 28 | import java.util.Iterator; 29 | import java.util.List; 30 | 31 | import static com.xebialabs.overthere.ConnectionOptions.*; 32 | 33 | public final class OverthereUtil { 34 | 35 | private OverthereUtil() { 36 | } 37 | 38 | public static ConnectionOptions fromQuery(URI url) { 39 | ConnectionOptions options = new ConnectionOptions(); 40 | options.set(ADDRESS, url.getHost()); 41 | if (url.getPort() > 0) { 42 | options.set(PORT, url.getPort()); 43 | } 44 | String user = url.getUserInfo(); 45 | String password = null; 46 | if (user == null) { 47 | user = System.getProperty("user.name"); 48 | } else { 49 | int idx = user.indexOf(':'); 50 | if (idx != -1) { 51 | password = user.substring(idx + 1); 52 | options.set(PASSWORD, password); 53 | user = user.substring(0, idx); 54 | } 55 | } 56 | options.set(USERNAME, user); 57 | List nvps = URLEncodedUtils.parse(url, StandardCharsets.UTF_8); 58 | for (NameValuePair nvp : nvps) { 59 | options.set(nvp.getName(), nvp.getValue()); 60 | } 61 | return options; 62 | } 63 | 64 | public static OverthereConnection overthereConnectionFromURI(String url) { 65 | try { 66 | return overthereConnectionFromURI(new URI(url)); 67 | } catch (URISyntaxException e) { 68 | throw new RuntimeException(e); 69 | } 70 | } 71 | 72 | public static OverthereConnection overthereConnectionFromURI(URI url) { 73 | ConnectionOptions options = fromQuery(url); 74 | return Overthere.getConnection(url.getScheme(), options); 75 | } 76 | 77 | /** 78 | * Copy files from srcHost to dstHost. Files are copied using overthere. So from file to file or from directory to 79 | * directory. If copySpec's length is even then files/directories are copied pair wise. If copySpec's lenght is odd 80 | * then everything is copied into the last entry. 81 | */ 82 | public static void copyFiles(OverthereConnection srcHost, OverthereConnection dstHost, List copySpec) { 83 | if (copySpec.isEmpty()) { 84 | return; 85 | } 86 | 87 | if (copySpec.size() % 2 == 0) { 88 | Iterator toCopy = copySpec.iterator(); 89 | while (toCopy.hasNext()) { 90 | OverthereFile src = srcHost.getFile(toCopy.next()); 91 | OverthereFile dst = dstHost.getFile(toCopy.next()); 92 | src.copyTo(dst); 93 | } 94 | } else { 95 | List srcFiles = copySpec.subList(0, copySpec.size() - 1); 96 | OverthereFile dst = dstHost.getFile(copySpec.get(copySpec.size() - 1)); 97 | 98 | for (String srcFile : srcFiles) { 99 | OverthereFile src = srcHost.getFile(srcFile); 100 | src.copyTo(dst); 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/Preconditions.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast; 17 | 18 | import static com.xebialabs.overcast.Strings.isNullOrEmpty; 19 | 20 | public final class Preconditions { 21 | private Preconditions() {} 22 | 23 | public static void checkState( 24 | boolean expression, 25 | String errorMessageTemplate, 26 | Object... errorMessageArgs) { 27 | if (!expression) { 28 | throw new IllegalStateException(String.format(errorMessageTemplate, errorMessageArgs)); 29 | } 30 | } 31 | 32 | public static void checkArgument( 33 | boolean expression, 34 | String errorMessageTemplate, 35 | Object... errorMessageArgs) { 36 | if (!expression) { 37 | throw new IllegalArgumentException(String.format(errorMessageTemplate, errorMessageArgs)); 38 | } 39 | } 40 | 41 | public static void checkNotNull(Object reference) { 42 | if (reference == null) { 43 | throw new NullPointerException(); 44 | } 45 | } 46 | 47 | public static void checkNotNull( 48 | Object reference, String errorMessageTemplate, Object... errorMessageArgs) { 49 | if (reference == null) { 50 | throw new NullPointerException(String.format(errorMessageTemplate, errorMessageArgs)); 51 | } 52 | } 53 | 54 | public static void checkNotNullOrEmpty(String s) { 55 | if (isNullOrEmpty(s)) { 56 | throw new NullPointerException(); 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/PropertiesLoader.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast; 17 | 18 | import com.typesafe.config.Config; 19 | import com.typesafe.config.ConfigFactory; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import java.io.File; 24 | import java.net.URISyntaxException; 25 | 26 | public class PropertiesLoader { 27 | 28 | private static final Logger logger = LoggerFactory.getLogger(PropertiesLoader.class); 29 | 30 | public static final String OVERCAST_CONF_FILE = "overcast.conf"; 31 | public static final String OVERCAST_USER_DIR = ".overcast"; 32 | public static final String OVERCAST_CONF_FILE_PROPERTY = "overcast.conf.file"; 33 | 34 | private static String getUserOvercastConfPath() { 35 | return new File(new File(System.getProperty("user.home"), OVERCAST_USER_DIR), OVERCAST_CONF_FILE).getAbsolutePath(); 36 | } 37 | 38 | public static Config loadOvercastConfig() { 39 | return ConfigFactory.systemProperties() 40 | .withFallback(loadOvercastConfigFromFile(getUserOvercastConfPath())) 41 | .withFallback(loadOvercastConfigFromFile(OVERCAST_CONF_FILE)) 42 | .withFallback(loadOvercastConfigFromFile(System.getProperty(OVERCAST_CONF_FILE_PROPERTY))) 43 | .withFallback(loadOvercastConfigFromClasspath(OVERCAST_CONF_FILE)) 44 | .resolve(); 45 | } 46 | 47 | /** Load {@link Config} from 'file' but do not resolve it. */ 48 | static Config loadOvercastConfigFromClasspath(String path) { 49 | try { 50 | // resource.toURI().getPath() so path gets URL decoded so spaces are no issue 51 | // using resource.getPath() runs into a bug with guava 52 | return loadOvercastConfigFromFile(Resources.getResource(path).toURI().getPath()); 53 | } catch (IllegalArgumentException | NullPointerException | URISyntaxException e) { 54 | logger.warn("File '{}' not found on classpath.", path); 55 | } 56 | return ConfigFactory.empty(); 57 | } 58 | 59 | /** Load {@link Config} from 'file' but do not resolve it. */ 60 | static Config loadOvercastConfigFromFile(String file) { 61 | 62 | if (file == null) { 63 | return ConfigFactory.empty(); 64 | } 65 | 66 | File f = new File(file); 67 | if (!f.exists()) { 68 | logger.warn("File {} not found.", f.getAbsolutePath()); 69 | return ConfigFactory.empty(); 70 | } 71 | 72 | logger.info("Loading from file {}", f.getAbsolutePath()); 73 | return ConfigFactory.parseFile(f); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/Resources.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast; 17 | 18 | import java.net.URL; 19 | 20 | import static com.xebialabs.overcast.Preconditions.checkArgument; 21 | 22 | public final class Resources { 23 | private Resources() {} 24 | 25 | public static URL getResource(String resourceName) { 26 | ClassLoader loader = Thread.currentThread().getContextClassLoader(); 27 | if (loader == null) { 28 | loader = Resources.class.getClassLoader(); 29 | } 30 | URL url = loader.getResource(resourceName); 31 | checkArgument(url != null, "resource %s not found.", resourceName); 32 | return url; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/Strings.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast; 17 | 18 | public final class Strings { 19 | private Strings() {} 20 | 21 | public static boolean isNullOrEmpty(String s) { 22 | return s == null || s.isEmpty(); 23 | } 24 | 25 | public static boolean nonEmpty(String s) { return !s.isEmpty(); } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/command/Command.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.command; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Arrays; 20 | import java.util.Iterator; 21 | import java.util.List; 22 | 23 | public class Command { 24 | 25 | private final List command = new ArrayList(); 26 | 27 | private Command() {} 28 | 29 | public static Command aCommand(String executable) { 30 | if (executable == null) { 31 | throw new IllegalArgumentException("Executable can not be null"); 32 | } 33 | Command c = new Command(); 34 | c.withPart(executable); 35 | return c; 36 | } 37 | 38 | public Command withPart(String... part) { 39 | if (part == null) { 40 | return this; 41 | } 42 | 43 | for (String p : part) { 44 | if (p != null) { 45 | command.add(p); 46 | } 47 | } 48 | return this; 49 | } 50 | 51 | public Command withArguments(String... argument) { 52 | return withPart(argument); 53 | } 54 | 55 | public Command withOptions(String... option) { 56 | return withPart(option); 57 | } 58 | 59 | public Command withPrefix(String prefix) { 60 | return withPart(prefix); 61 | } 62 | 63 | public List getCommand() { 64 | return command; 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | StringBuilder builder = new StringBuilder(); 70 | Iterator iterator = command.iterator(); 71 | if (iterator.hasNext()) { 72 | builder.append(iterator.next()); 73 | } 74 | while (iterator.hasNext()) { 75 | builder.append(' ').append(iterator.next()); 76 | } 77 | return builder.toString(); 78 | } 79 | 80 | public static Command fromString(String s) { 81 | Command c = new Command(); 82 | 83 | for (String part : s.split("\\s")) { 84 | c.getCommand().add(part); 85 | } 86 | 87 | return c; 88 | } 89 | 90 | @Override 91 | public boolean equals(final Object obj) { 92 | if (obj == null || !(obj instanceof Command)) { 93 | return false; 94 | } 95 | 96 | return this.toString().equals(obj.toString()); 97 | } 98 | 99 | @Override 100 | public int hashCode() { 101 | int c = 1000; 102 | 103 | for (char ch : this.toString().toCharArray()) { 104 | c += ch; 105 | } 106 | 107 | return c; 108 | } 109 | 110 | public List asList() { 111 | return Arrays.asList(getCommand().toArray(new String[getCommand().size()])); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/command/CommandProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.command; 17 | 18 | import java.io.*; 19 | import org.apache.commons.io.input.TeeInputStream; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | public class CommandProcessor { 24 | 25 | public static Logger logger = LoggerFactory.getLogger(CommandProcessor.class); 26 | 27 | private String execDir = "."; 28 | 29 | private CommandProcessor(final String execDir) { 30 | this.execDir = execDir; 31 | } 32 | 33 | private CommandProcessor() {} 34 | 35 | public static CommandProcessor atLocation(String l) { 36 | return new CommandProcessor(l); 37 | } 38 | 39 | public static CommandProcessor atCurrentDir() { 40 | return new CommandProcessor(); 41 | } 42 | 43 | public CommandResponse run(final Command command) { 44 | 45 | logger.debug("Executing command {}", command); 46 | 47 | try { 48 | Process p = new ProcessBuilder(command.asList()).directory(new File(execDir)).start(); 49 | 50 | // We do this small trick to have stdout and stderr of the process on the console and 51 | // at the same time capture them to strings. 52 | ByteArrayOutputStream errors = new ByteArrayOutputStream(); 53 | ByteArrayOutputStream messages = new ByteArrayOutputStream(); 54 | 55 | Thread t1 = showProcessOutput(new TeeInputStream(p.getErrorStream(), errors), System.err); 56 | Thread t2 = showProcessOutput(new TeeInputStream(p.getInputStream(), messages), System.out); 57 | 58 | int code = p.waitFor(); 59 | 60 | t1.join(); 61 | t2.join(); 62 | 63 | CommandResponse response = new CommandResponse(code, errors.toString(), messages.toString()); 64 | 65 | if (!response.isSuccessful()) { 66 | throw new NonZeroCodeException(command, response); 67 | } 68 | 69 | return response; 70 | 71 | } catch (InterruptedException e) { 72 | Thread.currentThread().interrupt(); 73 | throw new RuntimeException("Cannot execute " + command.toString(), e); 74 | } catch (IOException e) { 75 | throw new RuntimeException("Cannot execute " + command.toString(), e); 76 | } 77 | } 78 | 79 | private Thread showProcessOutput(final InputStream from, final PrintStream to) { 80 | Thread t = new Thread(() -> { 81 | try { 82 | for (; ; ) { 83 | int c = from.read(); 84 | if (c == -1) 85 | break; 86 | to.write((char) c); 87 | } 88 | } catch (IOException ignore) { 89 | } 90 | }); 91 | t.start(); 92 | return t; 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/command/CommandResponse.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.command; 17 | 18 | public class CommandResponse { 19 | 20 | private final int returnCode; 21 | 22 | private final String errors; 23 | 24 | private final String output; 25 | 26 | public CommandResponse(int returnCode, String errors, String output) { 27 | this.returnCode = returnCode; 28 | this.errors = errors; 29 | this.output = output; 30 | } 31 | 32 | public int getReturnCode() { 33 | return returnCode; 34 | } 35 | 36 | public String getErrors() { 37 | return errors; 38 | } 39 | 40 | public String getOutput() { 41 | return output; 42 | } 43 | 44 | public boolean isSuccessful() { 45 | return getReturnCode() == 0; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/command/NonZeroCodeException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.command; 17 | 18 | /** 19 | * Thrown when {@link Command} returns non zero code 20 | */ 21 | @SuppressWarnings("serial") 22 | public class NonZeroCodeException extends RuntimeException { 23 | 24 | private final Command command; 25 | 26 | private final CommandResponse response; 27 | 28 | public NonZeroCodeException(final Command command, final CommandResponse response) { 29 | super("Command " + command.toString() + " returned non-zero code " + response.getReturnCode()); 30 | this.command = command; 31 | this.response = response; 32 | } 33 | 34 | public Command getCommand() { 35 | return command; 36 | } 37 | 38 | public CommandResponse getResponse() { 39 | return response; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/host/CloudHost.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.host; 17 | 18 | /** 19 | * Represents a host in the cloud. 20 | */ 21 | public interface CloudHost { 22 | 23 | /** 24 | * Ensures the host is available. 25 | */ 26 | void setup(); 27 | 28 | /** 29 | * Releases the host resources. 30 | */ 31 | void teardown(); 32 | 33 | /** 34 | * Returns the name or IP address of the host to connect to. Can only be called after {@link #setup()} has been 35 | * invoked. 36 | * 37 | * @return the host name. 38 | */ 39 | String getHostName(); 40 | 41 | /** 42 | * Translates a target port number to the port number to connect to. Can only be called after {@link #setup()} has 43 | * been invoked. 44 | * 45 | * @param port 46 | * the target port number 47 | * @return the translated port number. 48 | */ 49 | int getPort(int port); 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/host/ExistingCloudHost.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.host; 17 | 18 | import static com.xebialabs.overcast.OvercastProperties.getOvercastProperty; 19 | 20 | class ExistingCloudHost implements CloudHost { 21 | 22 | private final String hostname; 23 | 24 | public ExistingCloudHost(String hostLabel) { 25 | this.hostname = getOvercastProperty(hostLabel + ".hostname", hostLabel); 26 | } 27 | 28 | @Override 29 | public void setup() { 30 | // no-op 31 | } 32 | 33 | @Override 34 | public void teardown() { 35 | // no-op 36 | } 37 | 38 | @Override 39 | public String getHostName() { 40 | return hostname; 41 | } 42 | 43 | @Override 44 | public int getPort(int port) { 45 | return port; 46 | } 47 | 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/host/VagrantCloudHost.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.host; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | import com.xebialabs.overcast.support.vagrant.VagrantDriver; 22 | import com.xebialabs.overcast.support.vagrant.VagrantState; 23 | 24 | import java.util.Map; 25 | 26 | import static com.xebialabs.overcast.support.vagrant.VagrantState.NOT_CREATED; 27 | import static com.xebialabs.overcast.support.vagrant.VagrantState.getTransitionCommand; 28 | 29 | public class VagrantCloudHost implements CloudHost { 30 | 31 | protected String vagrantIp; 32 | 33 | protected String vagrantVm; 34 | 35 | protected VagrantDriver vagrantDriver; 36 | 37 | private VagrantState initialState; 38 | 39 | private Map vagrantParameters; 40 | 41 | private static final Logger logger = LoggerFactory.getLogger(VagrantCloudHost.class); 42 | 43 | public VagrantCloudHost(String vagrantVm, String vagrantIp, VagrantDriver vagrantDriver, 44 | Map vagrantParameters) { 45 | this.vagrantIp = vagrantIp; 46 | this.vagrantDriver = vagrantDriver; 47 | this.vagrantVm = vagrantVm; 48 | this.vagrantParameters = vagrantParameters; 49 | } 50 | 51 | @Override 52 | public void setup() { 53 | initialState = vagrantDriver.state(vagrantVm); 54 | logger.info("Vagrant host is in state {}.", initialState.toString()); 55 | vagrantDriver.doVagrant(vagrantVm, getTransitionCommand(VagrantState.RUNNING, vagrantParameters)); 56 | } 57 | 58 | @Override 59 | public void teardown() { 60 | VagrantState nextState; 61 | if (initialState != null) { 62 | logger.info("Bringing vagrant back to {} state.", initialState.toString()); 63 | nextState = initialState; 64 | } else { 65 | logger.warn("No initial state was captured. Destroying the VM."); 66 | nextState = NOT_CREATED; 67 | } 68 | vagrantDriver.doVagrant(vagrantVm, getTransitionCommand(nextState, vagrantParameters)); 69 | } 70 | 71 | @Override 72 | public String getHostName() { 73 | return vagrantIp; 74 | } 75 | 76 | @Override 77 | public int getPort(int port) { 78 | return port; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/host/VirtualboxHost.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.host; 17 | 18 | import com.xebialabs.overcast.command.CommandProcessor; 19 | import com.xebialabs.overcast.support.virtualbox.VirtualboxDriver; 20 | 21 | public class VirtualboxHost implements CloudHost { 22 | 23 | private final String ip; 24 | private final String uuid; 25 | private final String snapshot; 26 | 27 | public VirtualboxHost(final String ip, final String uuid, final String snapshot) { 28 | this.ip = ip; 29 | this.uuid = uuid; 30 | this.snapshot = snapshot; 31 | } 32 | 33 | @Override 34 | public void setup() { 35 | new VirtualboxDriver(CommandProcessor.atCurrentDir()).loadSnapshot(uuid, snapshot); 36 | } 37 | 38 | @Override 39 | public void teardown() { 40 | new VirtualboxDriver(CommandProcessor.atCurrentDir()).powerOff(uuid); 41 | } 42 | 43 | @Override 44 | public String getHostName() { 45 | return ip; 46 | } 47 | 48 | @Override 49 | public int getPort(final int port) { 50 | return port; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/docker/Config.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.docker; 17 | 18 | public class Config { 19 | public static final String DOCKER_CERTIFICATES_SUFFIX = ".certificates"; 20 | public static final String DOCKER_HOST_SUFFIX = ".dockerHost"; 21 | public static final String DOCKER_IMAGE_SUFFIX = ".dockerImage"; 22 | public static final String DOCKER_NAME_SUFFIX = ".name"; 23 | public static final String DOCKER_COMMAND_SUFFIX = ".command"; 24 | public static final String DOCKER_REMOVE_SUFFIX = ".remove"; 25 | public static final String DOCKER_REMOVE_VOLUME_SUFFIX = ".removeVolume"; 26 | public static final String DOCKER_ENV_SUFFIX = ".env"; 27 | public static final String DOCKER_LINKS_SUFFIX = ".links"; 28 | public static final String DOCKER_EXPOSED_PORTS_SUFFIX = ".exposedPorts"; 29 | public static final String DOCKER_PORT_BINDINGS_SUFFIX = ".portBindings"; 30 | 31 | public static final String DOCKER_EXPOSE_ALL_PORTS_SUFFIX = ".exposeAllPorts"; 32 | public static final String DOCKER_TTY_SUFFIX = ".tty"; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/docker/ProcessHandlerLogger.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.docker; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | import com.spotify.docker.client.ProgressHandler; 21 | import com.spotify.docker.client.exceptions.DockerException; 22 | import com.spotify.docker.client.messages.ProgressMessage; 23 | 24 | public class ProcessHandlerLogger implements ProgressHandler { 25 | @Override 26 | public void progress(final ProgressMessage message) throws DockerException { 27 | logger.info(message.id() + " " + message.status()); 28 | } 29 | 30 | private static final Logger logger = LoggerFactory.getLogger(ProcessHandlerLogger.class); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/libvirt/Disk.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.libvirt; 17 | 18 | import java.io.IOException; 19 | import org.libvirt.LibvirtException; 20 | import org.libvirt.StoragePool; 21 | import org.libvirt.StorageVol; 22 | import org.libvirt.StorageVolInfo; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | import com.xebialabs.overcast.support.libvirt.jdom.DiskXml; 27 | 28 | import static com.xebialabs.overcast.Preconditions.checkNotNull; 29 | import static com.xebialabs.overcast.Preconditions.checkNotNullOrEmpty; 30 | 31 | public class Disk { 32 | private static final Logger log = LoggerFactory.getLogger(Disk.class); 33 | 34 | public String device; 35 | public String file; 36 | public String format; 37 | private final StorageVol volume; 38 | 39 | public Disk(String device, String file, StorageVol volume, String format) { 40 | checkNotNullOrEmpty(device); 41 | checkNotNullOrEmpty(file); 42 | checkNotNull(volume); 43 | checkNotNullOrEmpty(format); 44 | 45 | this.device = device; 46 | this.file = file; 47 | this.volume = volume; 48 | this.format = format; 49 | } 50 | 51 | public StorageVolInfo getInfo() { 52 | try { 53 | return volume.getInfo(); 54 | } catch (LibvirtException e) { 55 | throw new LibvirtRuntimeException(e); 56 | } 57 | } 58 | 59 | public String getName() { 60 | try { 61 | return volume.getName(); 62 | } catch (LibvirtException e) { 63 | throw new LibvirtRuntimeException(e); 64 | } 65 | } 66 | 67 | public String getBaseName() { 68 | String name = getName(); 69 | int idx = name.lastIndexOf('.'); 70 | if (idx == -1) { 71 | return name; 72 | } 73 | return name.substring(0, idx); 74 | } 75 | 76 | public StorageVol getVolume() { 77 | return volume; 78 | } 79 | 80 | public StoragePool getStoragePool() { 81 | try { 82 | return volume.storagePoolLookupByVolume(); 83 | } catch (LibvirtException e) { 84 | throw new LibvirtRuntimeException(e); 85 | } 86 | } 87 | 88 | public StorageVol createCloneWithBackingStore(String name) { 89 | try { 90 | String volumeXml = DiskXml.cloneVolumeXml(this, name); 91 | log.debug("Creating volume with xml={}", volumeXml); 92 | StorageVol vol = getStoragePool().storageVolCreateXML(volumeXml, 0); 93 | return vol; 94 | } catch (LibvirtException | IOException e) { 95 | throw new LibvirtRuntimeException(e); 96 | } 97 | } 98 | 99 | @Override 100 | public String toString() { 101 | return "Disk{" + 102 | "name=" + getName() + 103 | ", format='" + format + '\'' + 104 | ", file='" + file + '\'' + 105 | ", device='" + device + '\'' + 106 | '}'; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/libvirt/Filesystem.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.libvirt; 17 | 18 | import static com.xebialabs.overcast.Preconditions.checkNotNullOrEmpty; 19 | 20 | public class Filesystem { 21 | public enum AccessMode { 22 | /** The source is accessed with the permissions of the user inside the guest. This is the default. */ 23 | PASSTHROUGH, 24 | /** The source is accessed with the permissions of the hypervisor (QEMU process). */ 25 | MAPPED, 26 | /** 27 | * Similar to {@link AccessMode#PASSTHROUGH}, the exception is that failure of privileged operations like 28 | * 'chown' are ignored. This makes a passthrough-like mode usable for people who run the hypervisor as non-root. 29 | */ 30 | SQUASH, 31 | } 32 | 33 | public String source; 34 | public String target; 35 | public AccessMode accessMode; 36 | public boolean readOnly; 37 | 38 | public Filesystem(String source, String target, AccessMode accessMode, boolean readOnly) { 39 | checkNotNullOrEmpty(source); 40 | checkNotNullOrEmpty(target); 41 | 42 | this.source = source; 43 | this.target = target; 44 | this.accessMode = accessMode; 45 | this.readOnly = readOnly; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "Filesystem{" + 51 | "source='" + source + '\'' + 52 | ", target='" + target + '\'' + 53 | ", accessMode=" + accessMode + 54 | ", readOnly=" + readOnly + 55 | '}'; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/libvirt/IpLookupException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.libvirt; 17 | 18 | /** Indicates a failure occurred while an {@link IpLookupStrategy} was trying to look up the IP on a host. */ 19 | @SuppressWarnings("serial") 20 | public class IpLookupException extends RuntimeException { 21 | public IpLookupException(String message, Throwable cause) { 22 | super(message, cause); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/libvirt/IpLookupStrategy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.libvirt; 17 | 18 | public interface IpLookupStrategy { 19 | /** 20 | * attempt to lookup an IP for a MAC. May throw {@link IpLookupException} when the lookup fails abnormally. May 21 | * throw {@link IpNotFoundException} when the IP was not found. mac may be null if it is not required 22 | * for the lookup. 23 | */ 24 | String lookup(String mac); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/libvirt/IpNotFoundException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.libvirt; 17 | 18 | /** Indicates an {@link IpLookupStrategy} could not find the IP for a certain MAC. */ 19 | @SuppressWarnings("serial") 20 | public class IpNotFoundException extends RuntimeException { 21 | public IpNotFoundException(String message) { 22 | super(message); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/libvirt/JDomUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.libvirt; 17 | 18 | import java.io.ByteArrayInputStream; 19 | import java.io.IOException; 20 | import java.io.StringWriter; 21 | import java.nio.charset.StandardCharsets; 22 | 23 | import org.jdom2.Document; 24 | import org.jdom2.Element; 25 | import org.jdom2.JDOMException; 26 | import org.jdom2.Namespace; 27 | import org.jdom2.input.SAXBuilder; 28 | import org.jdom2.output.Format; 29 | import org.jdom2.output.XMLOutputter; 30 | 31 | public final class JDomUtil { 32 | private JDomUtil() { 33 | } 34 | 35 | public static String getElementText(Element parent, String localName, Namespace ns) { 36 | if (parent == null) { 37 | throw new IllegalArgumentException("parent element not found"); 38 | } 39 | Element child = parent.getChild(localName, ns); 40 | if (child == null) { 41 | throw new IllegalArgumentException(String.format("child element '%s' not found", localName)); 42 | } 43 | return child.getText(); 44 | } 45 | 46 | /** Convert xml to JDOM2 {@link Document}. Throws {@link IllegalArgumentException} in case of errors. */ 47 | public static Document stringToDocument(String xml) { 48 | try { 49 | SAXBuilder sax = new SAXBuilder(); 50 | return sax.build(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))); 51 | } catch (JDOMException | IOException e) { 52 | throw new IllegalArgumentException("Unable to parse xml", e); 53 | } 54 | } 55 | 56 | public static String documentToString(Document xml, Format format) throws IOException { 57 | StringWriter vsw = new StringWriter(); 58 | XMLOutputter xout = new XMLOutputter(format); 59 | xout.output(xml, vsw); 60 | return vsw.toString(); 61 | } 62 | 63 | public static String documentToString(Document xml) throws IOException { 64 | return documentToString(xml, Format.getPrettyFormat()); 65 | } 66 | 67 | public static String documentToRawString(Document xml) throws IOException { 68 | return documentToString(xml, Format.getRawFormat().setOmitDeclaration(true)).trim(); 69 | } 70 | 71 | public static String elementToString(Element element, Format format) throws IOException { 72 | StringWriter vsw = new StringWriter(); 73 | XMLOutputter xout = new XMLOutputter(format); 74 | xout.output(element, vsw); 75 | return vsw.toString(); 76 | } 77 | 78 | public static String elementToString(Element element) throws IOException { 79 | return elementToString(element, Format.getPrettyFormat()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/libvirt/LibvirtRuntimeException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.libvirt; 17 | 18 | @SuppressWarnings("serial") 19 | public class LibvirtRuntimeException extends RuntimeException { 20 | public LibvirtRuntimeException(Throwable e) { 21 | super(e); 22 | } 23 | 24 | public LibvirtRuntimeException(String message, Throwable e) { 25 | super(message, e); 26 | } 27 | 28 | public LibvirtRuntimeException(String message) { 29 | super(message); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/libvirt/LoggingOutputHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.libvirt; 17 | 18 | import org.slf4j.Logger; 19 | 20 | import com.xebialabs.overthere.OverthereExecutionOutputHandler; 21 | 22 | public class LoggingOutputHandler implements OverthereExecutionOutputHandler { 23 | private final Logger logger; 24 | private final String prefix; 25 | 26 | public LoggingOutputHandler(Logger logger, String prefix) { 27 | this.logger = logger; 28 | this.prefix = prefix; 29 | } 30 | 31 | @Override 32 | public void handleChar(char c) { 33 | // empty 34 | } 35 | 36 | @Override 37 | public void handleLine(String line) { 38 | logger.info("[{}] {}", prefix, line); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/libvirt/StaticIpLookupStrategy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.libvirt; 17 | 18 | import static com.xebialabs.overcast.OvercastProperties.getRequiredOvercastProperty; 19 | 20 | /** 21 | * Simple {@link IpLookupStrategy} that returns a pre-configured static IP. 22 | */ 23 | public class StaticIpLookupStrategy implements IpLookupStrategy { 24 | private static final String STATIC_IP_SUFFIX = ".static.ip"; 25 | 26 | private final String ip; 27 | 28 | public StaticIpLookupStrategy(String ip) { 29 | this.ip = ip; 30 | } 31 | 32 | public static StaticIpLookupStrategy create(String prefix) { 33 | String ip = getRequiredOvercastProperty(prefix + STATIC_IP_SUFFIX); 34 | return new StaticIpLookupStrategy(ip); 35 | } 36 | 37 | @Override 38 | public String lookup(String mac) { 39 | return ip; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/libvirt/jdom/DomainXml.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.libvirt.jdom; 17 | 18 | import org.jdom2.Document; 19 | import org.jdom2.Element; 20 | import org.jdom2.filter.Filters; 21 | import org.jdom2.xpath.XPathExpression; 22 | import org.jdom2.xpath.XPathFactory; 23 | 24 | public final class DomainXml { 25 | private DomainXml() { 26 | } 27 | 28 | public static Document setDomainName(Document domainXml, String name) { 29 | XPathFactory xpf = XPathFactory.instance(); 30 | 31 | XPathExpression nameExpr = xpf.compile("/domain/name", Filters.element()); 32 | Element nameElement = nameExpr.evaluateFirst(domainXml); 33 | nameElement.setText(name); 34 | 35 | return domainXml; 36 | } 37 | 38 | /** remove elements that need to be unique per clone. */ 39 | public static Document prepareForCloning(Document domainXml) { 40 | XPathFactory xpf = XPathFactory.instance(); 41 | 42 | // remove uuid so it will be generated 43 | domainXml.getRootElement().removeChild("uuid"); 44 | 45 | // remove mac address, so it will be generated 46 | XPathExpression macExpr = xpf.compile("/domain/devices/interface/mac", Filters.element()); 47 | for (Element mac : macExpr.evaluate(domainXml)) { 48 | mac.getParentElement().removeChild("mac"); 49 | } 50 | return domainXml; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/libvirt/jdom/FilesystemXml.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.libvirt.jdom; 17 | 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.Locale; 21 | import java.util.Map; 22 | 23 | import org.jdom2.Attribute; 24 | import org.jdom2.Document; 25 | import org.jdom2.Element; 26 | import org.jdom2.filter.Filters; 27 | import org.jdom2.xpath.XPathExpression; 28 | import org.jdom2.xpath.XPathFactory; 29 | 30 | import com.xebialabs.overcast.support.libvirt.Filesystem; 31 | import com.xebialabs.overcast.support.libvirt.Filesystem.AccessMode; 32 | 33 | public final class FilesystemXml { 34 | private static final String XPATH_FILESYSTEM = "/domain/devices/filesystem[@type='mount']"; 35 | 36 | private FilesystemXml() { 37 | } 38 | 39 | public static Element toFileSystemXml(Filesystem fs) { 40 | Element filesystem = new Element("filesystem") 41 | .setAttribute("type", "mount") 42 | .setAttribute("accessmode", fs.accessMode.name().toLowerCase()); 43 | 44 | filesystem.addContent(new Element("source").setAttribute("dir", fs.source)); 45 | filesystem.addContent(new Element("target").setAttribute("dir", fs.target)); 46 | if (fs.readOnly) { 47 | filesystem.addContent(new Element("readonly")); 48 | } 49 | return filesystem; 50 | } 51 | 52 | public static void removeFilesystemsWithTarget(Document domainXml, String targetDir) { 53 | XPathFactory xpf = XPathFactory.instance(); 54 | XPathExpression fsExpr = xpf.compile(String.format("/domain/devices/filesystem[@type='mount']/target[@dir='%s']", targetDir), Filters.element()); 55 | List tfs = fsExpr.evaluate(domainXml); 56 | for (Element e : tfs) { 57 | e.getParentElement().getParentElement().removeContent(e.getParentElement()); 58 | } 59 | } 60 | 61 | /** 62 | * Get map of {@link Filesystem}s. The key in the map is the target inside the domain. This will only return 63 | * filesystems of type 'mount'. 64 | */ 65 | public static Map getFilesystems(Document domainXml) { 66 | Map ret = new HashMap<>(); 67 | XPathFactory xpf = XPathFactory.instance(); 68 | XPathExpression fsExpr = xpf.compile(XPATH_FILESYSTEM, Filters.element()); 69 | List filesystems = fsExpr.evaluate(domainXml); 70 | for (Element fs : filesystems) { 71 | Attribute accessMode = fs.getAttribute("accessmode"); 72 | String source = fs.getChild("source").getAttribute("dir").getValue(); 73 | String target = fs.getChild("target").getAttribute("dir").getValue(); 74 | boolean readOnly = fs.getChild("readonly") != null; 75 | 76 | ret.put(target, new Filesystem(source, target, AccessMode.valueOf(accessMode.getValue().toUpperCase(Locale.US)), readOnly)); 77 | } 78 | return ret; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/libvirt/jdom/InterfaceXml.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.libvirt.jdom; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import org.jdom2.Document; 22 | import org.jdom2.Element; 23 | import org.jdom2.filter.Filters; 24 | import org.jdom2.xpath.XPathExpression; 25 | import org.jdom2.xpath.XPathFactory; 26 | import org.slf4j.Logger; 27 | import org.slf4j.LoggerFactory; 28 | 29 | public final class InterfaceXml { 30 | private static final Logger logger = LoggerFactory.getLogger(InterfaceXml.class); 31 | 32 | private InterfaceXml() { 33 | } 34 | 35 | /** 36 | * Get a map of mac addresses of interfaces defined on the domain. This is somewhat limited at the moment. It is 37 | * assumed that only one network interface with mac is connected to a bridge or network. For instance if you have a 38 | * bridged network device connected to 'br0' then you will find it's MAC address with the key 'br0'. 39 | */ 40 | public static Map getMacs(Document domainXml) { 41 | Map macs = new HashMap<>(); 42 | XPathFactory xpf = XPathFactory.instance(); 43 | XPathExpression interfaces = xpf.compile("/domain/devices/interface", Filters.element()); 44 | for (Element iface : interfaces.evaluate(domainXml)) { 45 | String interfaceType = iface.getAttribute("type").getValue(); 46 | logger.debug("Detecting IP on network of type '{}'", interfaceType); 47 | if ("bridge".equals(interfaceType)) { 48 | Element macElement = iface.getChild("mac"); 49 | String mac = macElement.getAttribute("address").getValue(); 50 | Element sourceElement = iface.getChild("source"); 51 | String bridge = sourceElement.getAttribute("bridge").getValue(); 52 | logger.info("Detected MAC '{}' on bridge '{}'", mac, bridge); 53 | macs.put(bridge, mac); 54 | } else if ("network".equals(interfaceType)) { 55 | Element macElement = iface.getChild("mac"); 56 | String mac = macElement.getAttribute("address").getValue(); 57 | Element sourceElement = iface.getChild("source"); 58 | String network = sourceElement.getAttribute("network").getValue(); 59 | logger.info("Detected MAC '{}' on network '{}'", mac, network); 60 | macs.put(network, mac); 61 | } else { 62 | logger.warn("Ignoring network of type {}", interfaceType); 63 | } 64 | } 65 | return macs; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/vagrant/VagrantDriver.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.vagrant; 17 | 18 | import com.xebialabs.overcast.command.CommandProcessor; 19 | import com.xebialabs.overcast.command.CommandResponse; 20 | 21 | import static com.xebialabs.overcast.command.Command.aCommand; 22 | 23 | public class VagrantDriver { 24 | 25 | private final String hostLabel; 26 | private final CommandProcessor commandProcessor; 27 | 28 | public VagrantDriver(String hostLabel, CommandProcessor commandProcessor) { 29 | this.hostLabel = hostLabel; 30 | this.commandProcessor = commandProcessor; 31 | } 32 | 33 | /** 34 | * Executes vagrant command which means that arguments passed here will be prepended with "vagrant" 35 | * @param vagrantCommand arguments for vagrant command 36 | * @return vagrant response object 37 | */ 38 | public CommandResponse doVagrant(String vagrantVm, final String... vagrantCommand) { 39 | CommandResponse response = commandProcessor.run( 40 | aCommand("vagrant").withArguments(vagrantCommand).withOptions(vagrantVm) 41 | ); 42 | 43 | if(!response.isSuccessful()) { 44 | throw new RuntimeException("Errors during vagrant execution: \n" + response.getErrors()); 45 | } 46 | 47 | // Check for puppet errors. Not vagrant still returns 0 when puppet fails 48 | // May not be needed after this PR released: https://github.com/mitchellh/vagrant/pull/1175 49 | for (String line : response.getOutput().split("\n\u001B")) { 50 | if (line.startsWith("[1;35merr:")) { 51 | throw new RuntimeException("Error in puppet output: " + line); 52 | } 53 | } 54 | 55 | return response; 56 | } 57 | 58 | public CommandResponse status(String vm) { 59 | return doVagrant(vm, "status"); 60 | } 61 | 62 | public VagrantState state(String vm) { 63 | return VagrantState.fromStatusString(status(vm).getOutput()); 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return hostLabel; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/vagrant/VagrantState.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.vagrant; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collection; 20 | import java.util.Map; 21 | 22 | public enum VagrantState { 23 | NOT_CREATED, POWEROFF, ABORTED, SAVED, RUNNING; 24 | 25 | public static VagrantState fromStatusString(String s) { 26 | if (s.contains("not created")) return NOT_CREATED; 27 | if (s.contains("poweroff")) return POWEROFF; 28 | if (s.contains("aborted")) return ABORTED; 29 | if (s.contains("saved")) return SAVED; 30 | if (s.contains("running")) return RUNNING; 31 | 32 | throw new RuntimeException("Unknown status: " + s); 33 | } 34 | 35 | public static String[] getTransitionCommand(VagrantState newState, Map vagrantParameters) { 36 | 37 | switch (newState) { 38 | case NOT_CREATED: 39 | return new String[]{"destroy", "-f"}; 40 | case POWEROFF: 41 | return new String[]{"halt"}; 42 | case SAVED: 43 | return new String[]{"suspend"}; 44 | case RUNNING: 45 | if(isNullOrEmpty(vagrantParameters)) { 46 | return new String[]{"up", "--provision"}; 47 | } else { 48 | ArrayList arr = new ArrayList<>(); 49 | for (var entry : vagrantParameters.entrySet()) { 50 | arr.add("--" + entry.getKey() + "=" + entry.getValue()); 51 | } 52 | arr.add("up"); 53 | arr.add("--provision"); 54 | return arr.toArray(new String[0]); 55 | } 56 | case ABORTED: 57 | break; // ignore 58 | default: 59 | throw new IllegalArgumentException(String.format("The state %s is not known", newState)); 60 | } 61 | throw new RuntimeException("Unexpected state in getTransitionCommand "+newState.name()); 62 | } 63 | private static boolean isNullOrEmpty(final Map< ?, ? > m) { 64 | return m == null || m.isEmpty(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/virtualbox/VirtualboxDriver.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.virtualbox; 17 | 18 | import com.xebialabs.overcast.command.CommandProcessor; 19 | 20 | import java.util.EnumSet; 21 | 22 | import static com.xebialabs.overcast.command.Command.aCommand; 23 | import static com.xebialabs.overcast.support.virtualbox.VirtualboxState.POWEROFF; 24 | import static com.xebialabs.overcast.support.virtualbox.VirtualboxState.SAVED; 25 | 26 | public class VirtualboxDriver { 27 | 28 | private final CommandProcessor commandProcessor; 29 | 30 | public VirtualboxDriver(final CommandProcessor commandProcessor) { 31 | this.commandProcessor = commandProcessor; 32 | } 33 | 34 | /** 35 | * Fetches VM state. 36 | */ 37 | public VirtualboxState vmState(String vm) { 38 | return VirtualboxState.fromStatusString(execute("showvminfo", vm)); 39 | } 40 | 41 | /** 42 | * Checks if VM exists. Accepts UUID or VM name as an argument. 43 | */ 44 | public boolean vmExists(final String vmOrUuid) { 45 | String[] lines = execute("list", "vms").split("\\s"); 46 | for (String line : lines) { 47 | if (line.endsWith("{" + vmOrUuid + "}") || line.startsWith("\"" + vmOrUuid + "\"")) 48 | return true; 49 | } 50 | return false; 51 | } 52 | 53 | /** 54 | * Shuts down if running, restores the snapshot and starts VM. 55 | */ 56 | public void loadSnapshot(String vm, String snapshotUuid) { 57 | if (!EnumSet.of(POWEROFF, SAVED).contains(vmState(vm))) { 58 | powerOff(vm); 59 | } 60 | execute("snapshot", vm, "restore", snapshotUuid); 61 | start(vm); 62 | } 63 | 64 | /** 65 | * Shuts down if running, restores the latest snapshot and starts VM. 66 | */ 67 | public void loadLatestSnapshot(final String vm) { 68 | String quotedId = null; 69 | String[] lines = execute("snapshot", vm, "list", "--machinereadable").split("\n"); 70 | for (String line : lines) { 71 | if (!line.isEmpty()) { 72 | String[] parts = line.split("="); 73 | if (parts[0].equals("CurrentSnapshotUUID")) { 74 | quotedId = parts[1]; 75 | } 76 | } 77 | } 78 | 79 | if (quotedId != null) { 80 | loadSnapshot(vm, quotedId.substring(1, quotedId.length() - 1)); 81 | } 82 | } 83 | 84 | /** 85 | * Shuts down VM. 86 | */ 87 | public void powerOff(final String vm) { 88 | execute("controlvm", vm, "poweroff"); 89 | } 90 | 91 | public void start(String vm) { 92 | execute("startvm", vm, "--type", "headless"); 93 | } 94 | 95 | /** 96 | * Executes custom VBoxManage command 97 | */ 98 | public String execute(String... command) { 99 | return commandProcessor.run(aCommand("VBoxManage").withArguments(command)).getOutput(); 100 | } 101 | 102 | /** 103 | * Sets extra data on the VM 104 | */ 105 | public void setExtraData(String vm, String k, String v) { 106 | execute("setextradata", vm, k, v); 107 | } 108 | 109 | public String getExtraData(String vm, String k) { 110 | final String prefix = "Value: "; 111 | 112 | String v = execute("getextradata", vm, k).trim(); 113 | return v.equals("No value set!") ? null : v.substring(prefix.length()); 114 | } 115 | 116 | public void createSnapshot(String vm, String name) { 117 | execute("snapshot", vm, "take", name, "--description", "'Snapshot taken by Overcast.'"); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/virtualbox/VirtualboxState.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.virtualbox; 17 | 18 | public enum VirtualboxState { 19 | POWEROFF, ABORTED, SAVED, RUNNING; 20 | 21 | 22 | public static VirtualboxState fromStatusString(String s) { 23 | 24 | String stateString = findState(s); 25 | 26 | 27 | if (stateString.contains("powered off")) { 28 | return POWEROFF; 29 | } 30 | 31 | if (stateString.contains("saved")) { 32 | return POWEROFF; 33 | } 34 | 35 | if (stateString.contains("aborted")) { 36 | return ABORTED; 37 | } 38 | 39 | if (stateString.contains("running")) { 40 | return RUNNING; 41 | } 42 | 43 | throw new IllegalStateException("Can not detect state for state string: " + stateString); 44 | } 45 | 46 | private static String findState(String s) { 47 | String[] lines = s.split("\n"); 48 | for (String line : lines) { 49 | if (line.startsWith("State:")) { 50 | return line; 51 | } 52 | } 53 | throw new IllegalArgumentException("Expected 'State:...' but was: '" + s + "'."); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/vmware/JsonBodyHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.vmware; 17 | 18 | import com.fasterxml.jackson.databind.ObjectMapper; 19 | 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.io.UncheckedIOException; 23 | import java.net.http.HttpResponse; 24 | import java.util.function.Supplier; 25 | 26 | public class JsonBodyHandler implements HttpResponse.BodyHandler> { 27 | 28 | private final Class wClass; 29 | 30 | public JsonBodyHandler(Class wClass) { 31 | this.wClass = wClass; 32 | } 33 | 34 | @Override 35 | public HttpResponse.BodySubscriber> apply(HttpResponse.ResponseInfo responseInfo) { 36 | return asJSON(wClass); 37 | } 38 | 39 | private static HttpResponse.BodySubscriber> asJSON(Class targetType) { 40 | HttpResponse.BodySubscriber upstream = HttpResponse.BodySubscribers.ofInputStream(); 41 | 42 | return HttpResponse.BodySubscribers.mapping( 43 | upstream, 44 | inputStream -> toSupplierOfType(inputStream, targetType)); 45 | } 46 | 47 | private static Supplier toSupplierOfType(InputStream inputStream, Class targetType) { 48 | return () -> { 49 | try (InputStream stream = inputStream) { 50 | ObjectMapper objectMapper = new ObjectMapper(); 51 | return objectMapper.readValue(stream, targetType); 52 | } catch (IOException e) { 53 | throw new UncheckedIOException(e); 54 | } 55 | }; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/support/vmware/VMWareVM.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.vmware; 17 | 18 | public class VMWareVM { 19 | 20 | private final Integer cpuCount; 21 | private final String id; 22 | private final Integer memorySizeMb; 23 | private final String name; 24 | private final String powerState; 25 | 26 | public VMWareVM(Integer memorySizeMb, String id, String name, String powerState, Integer cpuCount) { 27 | this.memorySizeMb = memorySizeMb; 28 | this.id = id; 29 | this.name = name; 30 | this.powerState = powerState; 31 | this.cpuCount = cpuCount; 32 | } 33 | 34 | public Integer getMemorySizeMb() { 35 | return memorySizeMb; 36 | } 37 | 38 | public String getId() { 39 | return id; 40 | } 41 | 42 | public String getName() { 43 | return name; 44 | } 45 | 46 | public String getPowerState() { 47 | return powerState; 48 | } 49 | 50 | public Integer getCpuCount() { 51 | return cpuCount; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/xebialabs/overcast/util/RetryCommand.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.util; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | import java.util.concurrent.TimeUnit; 22 | import java.util.function.Supplier; 23 | 24 | public class RetryCommand { 25 | 26 | public static Logger logger = LoggerFactory.getLogger(RetryCommand.class); 27 | 28 | private final int maxRetries; 29 | 30 | public RetryCommand(int maxRetries) { 31 | this.maxRetries = maxRetries; 32 | } 33 | 34 | public T run(Supplier function) { 35 | try { 36 | return function.get(); 37 | } catch (Exception e) { 38 | logger.info(e.getMessage()); 39 | sleep(); 40 | return retry(function); 41 | } 42 | } 43 | 44 | private T retry(Supplier function) { 45 | int retryCounter = 0; 46 | while (retryCounter < maxRetries) { 47 | try { 48 | return function.get(); 49 | } catch (Exception ex) { 50 | retryCounter++; 51 | sleep(); 52 | logger.info(ex.getMessage()); 53 | if (retryCounter >= maxRetries) { 54 | break; 55 | } 56 | } 57 | } 58 | throw new RuntimeException("Command failed on all of " + maxRetries + " retries"); 59 | } 60 | 61 | private void sleep() { 62 | try { 63 | TimeUnit.SECONDS.sleep(5); 64 | } catch (InterruptedException e) { 65 | throw new RuntimeException(e); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/test/java/com/xebialabs/overcast/OverthereUtilTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast; 17 | 18 | import com.xebialabs.overthere.ConnectionOptions; 19 | import com.xebialabs.overthere.OperatingSystemFamily; 20 | import com.xebialabs.overthere.OverthereConnection; 21 | import com.xebialabs.overthere.local.LocalConnection; 22 | import com.xebialabs.overthere.ssh.SshConnectionType; 23 | import org.junit.jupiter.api.Test; 24 | 25 | import java.io.File; 26 | import java.io.IOException; 27 | import java.net.URI; 28 | import java.nio.file.Files; 29 | import java.util.Arrays; 30 | 31 | import static com.xebialabs.overthere.ConnectionOptions.*; 32 | import static com.xebialabs.overthere.OperatingSystemFamily.UNIX; 33 | import static com.xebialabs.overthere.ssh.SshConnectionBuilder.*; 34 | import static com.xebialabs.overthere.ssh.SshConnectionType.SFTP; 35 | import static org.hamcrest.CoreMatchers.equalTo; 36 | import static org.hamcrest.MatcherAssert.assertThat; 37 | 38 | public class OverthereUtilTest { 39 | 40 | public void deleteRecursively(File f) { 41 | if (f.isDirectory()) { 42 | for (File c : f.listFiles()) { 43 | deleteRecursively(c); 44 | } 45 | } 46 | f.delete(); 47 | } 48 | 49 | @Test 50 | public void testGetOvercastProperty() throws Exception { 51 | String url = "ssh://user:secret@localhost?os=UNIX&connectionType=SFTP&privateKeyFile=privateKey&passphrase=mypass"; 52 | ConnectionOptions options = OverthereUtil.fromQuery(new URI(url)); 53 | 54 | assertThat(options.getEnum(OPERATING_SYSTEM, OperatingSystemFamily.class), equalTo(UNIX)); 55 | assertThat(options.getEnum(CONNECTION_TYPE, SshConnectionType.class), equalTo(SFTP)); 56 | assertThat(options.get(PRIVATE_KEY_FILE), equalTo("privateKey")); 57 | assertThat(options.get(PASSPHRASE), equalTo("mypass")); 58 | assertThat(options.get(ADDRESS), equalTo("localhost")); 59 | assertThat(options.get(USERNAME), equalTo("user")); 60 | assertThat(options.get(PASSWORD), equalTo("secret")); 61 | } 62 | 63 | @Test 64 | public void testCopyFile() throws IOException { 65 | File tempdir = Files.createTempDirectory("overthere-util-test").toFile(); 66 | try { 67 | OverthereConnection srcHost = LocalConnection.getLocalConnection(); 68 | OverthereConnection dstHost = LocalConnection.getLocalConnection(); 69 | 70 | File dstFile = new File(tempdir, "destfile"); 71 | OverthereUtil.copyFiles(srcHost, dstHost, Arrays.asList("src/test/resources/copyFilesTest/1/file", dstFile.getAbsolutePath())); 72 | assertThat(dstFile.exists(), equalTo(true)); 73 | } finally { 74 | deleteRecursively(tempdir); 75 | } 76 | } 77 | 78 | @Test 79 | public void testCopyDir() throws IOException { 80 | File tempdir = Files.createTempDirectory("overthere-util-test").toFile(); 81 | try { 82 | OverthereConnection srcHost = LocalConnection.getLocalConnection(); 83 | OverthereConnection dstHost = LocalConnection.getLocalConnection(); 84 | 85 | OverthereUtil.copyFiles(srcHost, dstHost, Arrays.asList("src/test/resources/copyFilesTest/1", tempdir.getAbsolutePath())); 86 | 87 | File dstFile = new File(tempdir, "file"); 88 | assertThat(dstFile.exists(), equalTo(true)); 89 | } finally { 90 | deleteRecursively(tempdir); 91 | } 92 | } 93 | 94 | @Test 95 | public void testCopyMultiple() throws IOException { 96 | File tempdir = Files.createTempDirectory("overthere-util-test").toFile(); 97 | try { 98 | OverthereConnection srcHost = LocalConnection.getLocalConnection(); 99 | OverthereConnection dstHost = LocalConnection.getLocalConnection(); 100 | 101 | OverthereUtil.copyFiles(srcHost, dstHost, 102 | Arrays.asList("src/test/resources/copyFilesTest/1", "src/test/resources/copyFilesTest/2", tempdir.getAbsolutePath())); 103 | 104 | File dstFiles[] = new File[]{new File(tempdir, "file"), new File(tempdir, "file2")}; 105 | for (File dstFile : dstFiles) { 106 | assertThat(dstFile.toString() + " should exist", dstFile.exists(), equalTo(true)); 107 | } 108 | } finally { 109 | deleteRecursively(tempdir); 110 | } 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/test/java/com/xebialabs/overcast/PropertiesLoaderTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast; 17 | 18 | import com.typesafe.config.Config; 19 | import org.junit.jupiter.api.Test; 20 | 21 | import static org.hamcrest.CoreMatchers.is; 22 | import static org.hamcrest.CoreMatchers.notNullValue; 23 | import static org.hamcrest.MatcherAssert.assertThat; 24 | import static org.junit.jupiter.api.Assertions.assertNotNull; 25 | import static org.junit.jupiter.api.Assertions.assertTrue; 26 | 27 | public class PropertiesLoaderTest { 28 | 29 | @Test 30 | public void shouldLoadPropertiesFromPathWithSpace() { 31 | Config cfg = PropertiesLoader.loadOvercastConfigFromFile("src/test/resources/with space/test.conf").resolve(); 32 | assertThat(cfg.getString("foo"), is("bar")); 33 | } 34 | 35 | @Test 36 | public void shouldLoadPropertiesFromClassPathWithSpace() { 37 | Config cfg = PropertiesLoader.loadOvercastConfigFromClasspath("with space/test.conf").resolve(); 38 | assertThat(cfg.getString("foo"), is("bar")); 39 | } 40 | 41 | @Test 42 | public void shouldLoadPropertiesFromPath() { 43 | Config cfg = PropertiesLoader.loadOvercastConfigFromFile("src/test/resources/overcast.conf").resolve(); 44 | assertThat(cfg.getString("unittestHost.someProp"), is("someValue")); 45 | } 46 | 47 | @Test 48 | public void shouldLoadEmptyPropertiesFromNull() { 49 | Config cfg = PropertiesLoader.loadOvercastConfigFromFile(null).resolve(); 50 | assertNotNull(cfg); 51 | assertTrue(cfg.isEmpty()); 52 | } 53 | 54 | @Test 55 | public void shouldLoadPropertiesFromClassPath() { 56 | Config cfg = PropertiesLoader.loadOvercastConfigFromClasspath("overcast.conf").resolve(); 57 | assertThat(cfg.getString("unittestHost.someProp"), is("someValue")); 58 | } 59 | 60 | @Test 61 | public void shouldLoadConfigFromClassPath() { 62 | Config config = PropertiesLoader.loadOvercastConfigFromClasspath("overcast.conf").resolve(); 63 | boolean isWin = System.getProperty("os.name").contains("Windows"); 64 | assertThat(config, notNullValue()); 65 | assertThat(config.hasPath("some.nested.namespace.stringproperty"), is(true)); 66 | assertThat(config.getString("some.nested.namespace.stringproperty"), is("somevalue")); 67 | assertThat(config.hasPath("some.intprop"), is(true)); 68 | assertThat(config.getInt("some.intprop"), is(42)); 69 | assertThat(config.getInt("another.namespace.copiedValue"), is(42)); 70 | if (isWin) { 71 | assertThat(config.getString("another.namespace.winHome"), is(System.getenv("HOMEDRIVE") + System.getenv("HOMEPATH"))); 72 | assertThat(config.hasPath("another.namespace.unixHome"), is(false)); 73 | } else { 74 | assertThat(config.getString("another.namespace.unixHome"), is(System.getenv("HOME"))); 75 | assertThat(config.getString("another.namespace.winHome"), is("")); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/test/java/com/xebialabs/overcast/command/CommandProcessorTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.command; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import static com.xebialabs.overcast.command.Command.aCommand; 21 | import static com.xebialabs.overcast.command.CommandProcessor.atLocation; 22 | import static org.hamcrest.CoreMatchers.is; 23 | import static org.hamcrest.MatcherAssert.assertThat; 24 | import static org.junit.jupiter.api.Assertions.assertThrows; 25 | import static org.junit.jupiter.api.Assumptions.assumeTrue; 26 | 27 | public class CommandProcessorTest { 28 | 29 | @Test 30 | public void shouldThrowExceptionWhenCommandFailed() { 31 | //Test only for UNIX 32 | assumeTrue(System.getenv().containsKey("PATH")); 33 | 34 | assertThrows(NonZeroCodeException.class, () -> { 35 | atLocation("/tmp").run(aCommand("ls").withArguments("-wrong-argument")); 36 | }); 37 | } 38 | 39 | @Test 40 | public void shouldStoreOutput() { 41 | //Test only for UNIX 42 | assumeTrue(System.getenv().containsKey("PATH")); 43 | CommandResponse ls = atLocation("/tmp").run(aCommand("ls")); 44 | assertThat(ls.getReturnCode(), is(0)); 45 | assertThat(ls.getOutput().length() > 0, is(true)); 46 | } 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/com/xebialabs/overcast/host/DockerHostTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.host; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import static org.hamcrest.CoreMatchers.is; 21 | import static org.hamcrest.MatcherAssert.assertThat; 22 | 23 | public class DockerHostTest { 24 | @Test 25 | public void shouldReturnLocalhostForUnixSocket() { 26 | DockerHost dh = new DockerHost("image", "unix:///var/run/docker", null); 27 | assertThat("localhost", is(dh.getHostName())); 28 | } 29 | 30 | @Test 31 | public void shouldReturnOtherHostForUrl() { 32 | DockerHost dh = new DockerHost("image", "http://remotehost", null); 33 | assertThat("remotehost", is(dh.getHostName())); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/com/xebialabs/overcast/support/libvirt/jdom/DomainXmlTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.libvirt.jdom; 17 | 18 | import com.xebialabs.overcast.Resources; 19 | import org.jdom2.Document; 20 | import org.jdom2.input.SAXBuilder; 21 | import org.junit.jupiter.api.Test; 22 | 23 | import static org.hamcrest.CoreMatchers.notNullValue; 24 | import static org.hamcrest.CoreMatchers.nullValue; 25 | import static org.hamcrest.MatcherAssert.assertThat; 26 | 27 | public class DomainXmlTest { 28 | 29 | public Document getXml(String file) throws Exception { 30 | SAXBuilder saxBuilder = new SAXBuilder(); 31 | return saxBuilder.build(Resources.getResource(file)); 32 | } 33 | 34 | @Test 35 | public void shouldPrepareForCloning() throws Exception { 36 | Document domainXml = getXml("libvirt-xml/simple-domain.xml"); 37 | 38 | assertThat(domainXml.getRootElement().getChild("uuid"), notNullValue()); 39 | assertThat(domainXml.getRootElement().getChild("devices").getChild("interface").getChild("mac"), notNullValue()); 40 | 41 | DomainXml.prepareForCloning(domainXml); 42 | 43 | assertThat(domainXml.getRootElement().getChild("uuid"), nullValue()); 44 | assertThat(domainXml.getRootElement().getChild("devices").getChild("interface").getChild("mac"), nullValue()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/com/xebialabs/overcast/support/libvirt/jdom/FilesystemXmlTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.libvirt.jdom; 17 | 18 | import java.util.Collections; 19 | import java.util.Map; 20 | 21 | import com.xebialabs.overcast.Resources; 22 | import org.jdom2.Document; 23 | import org.jdom2.input.SAXBuilder; 24 | 25 | import com.xebialabs.overcast.support.libvirt.Filesystem; 26 | import org.junit.jupiter.api.Test; 27 | 28 | import static org.hamcrest.CoreMatchers.equalTo; 29 | import static org.hamcrest.MatcherAssert.assertThat; 30 | import static org.hamcrest.Matchers.hasKey; 31 | import static org.hamcrest.Matchers.hasSize; 32 | 33 | public class FilesystemXmlTest { 34 | 35 | public Document getXml(String file) throws Exception { 36 | SAXBuilder saxBuilder = new SAXBuilder(); 37 | return saxBuilder.build(Resources.getResource(file)); 38 | } 39 | 40 | @Test 41 | public void shouldReadNoFilesystems() throws Exception { 42 | Document domainXml = getXml("libvirt-xml/simple-domain.xml"); 43 | Map fs = FilesystemXml.getFilesystems(domainXml); 44 | assertThat(fs, equalTo(Collections.EMPTY_MAP)); 45 | } 46 | 47 | @Test 48 | public void shouldReadFilesystems() throws Exception { 49 | Document domainXml = getXml("libvirt-xml/domain-with-filesystem.xml"); 50 | 51 | Map fs = FilesystemXml.getFilesystems(domainXml); 52 | assertThat(fs.keySet(), hasSize(2)); 53 | 54 | assertThat(fs, hasKey("/vagrant")); 55 | assertThat(fs.get("/vagrant").readOnly, equalTo(true)); 56 | 57 | assertThat(fs, hasKey("/data")); 58 | assertThat(fs.get("/data").readOnly, equalTo(false)); 59 | assertThat(fs.get("/data").source, equalTo("/mnt/data")); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/com/xebialabs/overcast/support/vagrant/VagrantCloudHostTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.vagrant; 17 | 18 | import com.xebialabs.overcast.command.CommandResponse; 19 | import com.xebialabs.overcast.host.VagrantCloudHost; 20 | import org.junit.jupiter.api.BeforeEach; 21 | import org.junit.jupiter.api.Test; 22 | import org.mockito.Mock; 23 | import org.mockito.MockitoAnnotations; 24 | 25 | import static com.xebialabs.overcast.support.vagrant.VagrantState.NOT_CREATED; 26 | import static org.junit.jupiter.api.Assertions.assertThrows; 27 | import static org.mockito.Mockito.when; 28 | 29 | public class VagrantCloudHostTest { 30 | 31 | @Mock 32 | private VagrantDriver vagrantDriver; 33 | 34 | @BeforeEach 35 | public void setUp() { 36 | MockitoAnnotations.initMocks(this); 37 | } 38 | 39 | @Test 40 | public void shouldThrowNoExceptionsWhenAllGoesFine() { 41 | when(vagrantDriver.state("vm")).thenReturn(NOT_CREATED); 42 | when(vagrantDriver.doVagrant("vm", "up")).thenReturn(new CommandResponse(0, "", "")); 43 | 44 | VagrantCloudHost vagrantCloudHost = new VagrantCloudHost("vm", "127.0.0.1", vagrantDriver, null); 45 | 46 | vagrantCloudHost.setup(); 47 | } 48 | 49 | @Test 50 | public void shouldThrowAnExceptionWhenExitCodeIsNot0() { 51 | assertThrows(RuntimeException.class, () -> { 52 | when(vagrantDriver.doVagrant("vm", "status")).thenReturn(new CommandResponse(0, "", "not created")); 53 | when(vagrantDriver.doVagrant("vm", "up")).thenReturn(new CommandResponse(3, "", "")); 54 | 55 | VagrantCloudHost vagrantCloudHost = new VagrantCloudHost("vm", "127.0.0.1", vagrantDriver, null); 56 | vagrantCloudHost.setup(); 57 | }); 58 | } 59 | 60 | 61 | @Test 62 | public void shouldThrowAnExceptionWhenOutputContainsPuppetErrors() { 63 | assertThrows(RuntimeException.class, () -> { 64 | String outputWithPuppetErrors = "\u001B[0;36mnotice: /Stage[main]/Deployit-mw::blablabla\n" + 65 | "\u001B[1;35merr: /Stage[main]/Was::Config/Exec[call url]/returns: blablabla[0m\n" + 66 | "\u001B[0;36mnotice: /Stage[main]/Was::Config/Exec[configure-was]: Dependency blablabla[0m\n" + 67 | "\u001B[0;33mwarning: /Stage[main]/Was::Config/Exec[configure-was]: Skipping because of blablabla[0m\n" + 68 | "\u001B[0;36mnotice: Finished catalog run in 593.85 seconds[0m"; 69 | 70 | when(vagrantDriver.doVagrant("status")).thenReturn(new CommandResponse(0, "", "not created")); 71 | when(vagrantDriver.doVagrant("up")).thenReturn(new CommandResponse(0, "", outputWithPuppetErrors)); 72 | 73 | VagrantCloudHost vagrantCloudHost = new VagrantCloudHost("vm", "127.0.0.1", vagrantDriver, null); 74 | vagrantCloudHost.setup(); 75 | }); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/test/java/com/xebialabs/overcast/support/vagrant/VagrantStateTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.vagrant; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | 23 | import static com.xebialabs.overcast.support.vagrant.VagrantState.*; 24 | import static org.junit.jupiter.api.Assertions.assertEquals; 25 | 26 | public class VagrantStateTest { 27 | 28 | @Test 29 | public void shouldTranslateStatusStringToTest() { 30 | assertEquals( 31 | RUNNING, 32 | fromStatusString("Current VM states:\n" + 33 | "\n" + 34 | "default running\n" + 35 | "\n" + 36 | "The VM is running. To stop this VM, you can run `vagrant halt` to\n" + 37 | "shut it down forcefully, or you can run `vagrant suspend` to simply\n" + 38 | "suspend the virtual machine. In either case, to restart it again,\n" + 39 | "simply run `vagrant up`.") 40 | ); 41 | 42 | assertEquals(NOT_CREATED, fromStatusString("default not created\n")); 43 | assertEquals(POWEROFF, fromStatusString("default poweroff\n")); 44 | assertEquals(ABORTED, fromStatusString("default aborted\n")); 45 | assertEquals(SAVED, fromStatusString("default saved\n")); 46 | } 47 | 48 | @Test 49 | public void shouldGiveATransitionCommand() { 50 | Map vagrantParameters = new HashMap<>() {{ 51 | put("key1", "value1"); 52 | put("key2", "value2"); 53 | }}; 54 | assertEquals("up", getTransitionCommand(RUNNING, null)[0]); 55 | assertEquals("--key1=value1", getTransitionCommand(RUNNING, vagrantParameters)[0]); 56 | assertEquals("--key2=value2", getTransitionCommand(RUNNING, vagrantParameters)[1]); 57 | assertEquals("up", getTransitionCommand(RUNNING, vagrantParameters)[2]); 58 | assertEquals("halt", getTransitionCommand(POWEROFF, null)[0]); 59 | assertEquals("suspend", getTransitionCommand(SAVED, null)[0]); 60 | assertEquals("destroy", getTransitionCommand(NOT_CREATED, null)[0]); 61 | assertEquals("-f", getTransitionCommand(NOT_CREATED, null)[1]); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/com/xebialabs/overcast/support/virtualbox/VirtualboxStateTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2021 Digital.ai 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 com.xebialabs.overcast.support.virtualbox; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import static org.hamcrest.CoreMatchers.is; 21 | import static org.hamcrest.MatcherAssert.assertThat; 22 | 23 | public class VirtualboxStateTest { 24 | 25 | @Test 26 | public void shouldRecognizePowerOffState() { 27 | String s = "Nested Paging: on\n" + 28 | "State: powered off (since 2013-03-26T17:33:45.000000000)\n" + 29 | "Monitor count: 1\n" + 30 | "3D Acceleration: off"; 31 | 32 | assertThat(VirtualboxState.fromStatusString(s), is(VirtualboxState.POWEROFF)); 33 | } 34 | 35 | @Test 36 | public void shouldRecognizeAbortedState() { 37 | String s = "Nested Paging: on\n" + 38 | "State: aborted (since 2013-03-26T17:33:45.000000000)\n" + 39 | "Monitor count: 1\n" + 40 | "3D Acceleration: off"; 41 | 42 | assertThat(VirtualboxState.fromStatusString(s), is(VirtualboxState.ABORTED)); 43 | } 44 | 45 | @Test 46 | public void shouldRecognizeRunningState() { 47 | String s = "Nested Paging: on\n" + 48 | "State: running (since 2013-03-26T17:33:45.000000000)\n" + 49 | "Monitor count: 1\n" + 50 | "3D Acceleration: off"; 51 | 52 | assertThat(VirtualboxState.fromStatusString(s), is(VirtualboxState.RUNNING)); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/resources/copyFilesTest/1/file: -------------------------------------------------------------------------------- 1 | nothing interesting here. -------------------------------------------------------------------------------- /src/test/resources/copyFilesTest/2/file2: -------------------------------------------------------------------------------- 1 | nothing interesting here. -------------------------------------------------------------------------------- /src/test/resources/copyFilesTest/2/sub/file3: -------------------------------------------------------------------------------- 1 | foo -------------------------------------------------------------------------------- /src/test/resources/dir-without-conf/no-config-here: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xebialabs/overcast/8a494dc8f8572ae337ce91b06829eb9567c91f5d/src/test/resources/dir-without-conf/no-config-here -------------------------------------------------------------------------------- /src/test/resources/fake-home/.overcast/overcast.conf: -------------------------------------------------------------------------------- 1 | precedenceTestValue=valueFromHome 2 | -------------------------------------------------------------------------------- /src/test/resources/libvirt-xml/domain-with-filesystem.xml: -------------------------------------------------------------------------------- 1 | 18 | 19 | domain-with-filesystem 20 | 9d0f600e-d24f-1ea2-0afe-1c84826de376 21 | Domain with file system 22 | 2097152 23 | 2097152 24 | 1 25 | 26 | hvm 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | destroy 36 | restart 37 | restart 38 | 39 | /usr/bin/kvm 40 | 41 | 42 | 43 | 44 |
45 | 46 | 47 | 48 | 49 | 50 |
51 | 52 | 53 |
54 | 55 | 56 |
57 | 58 | 59 | 60 | 61 | 62 |
63 | 64 | 65 | 66 | 67 | 68 |
69 | 70 | 71 | 72 | 73 | 74 |
75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 |