fr.treeptik.cloudunit.modules.AbstractModuleControllerTestIT.java Source code

Java tutorial

Introduction

Here is the source code for fr.treeptik.cloudunit.modules.AbstractModuleControllerTestIT.java

Source

/*
 * LICENCE : CloudUnit is available under the Affero Gnu Public License GPL V3 : https://www.gnu.org/licenses/agpl-3.0.html
 *     but CloudUnit is licensed too under a standard commercial license.
 *     Please contact our sales team if you would like to discuss the specifics of our Enterprise license.
 *     If you are not sure whether the GPL is right for you,
 *     you can always test our software under the GPL and inspect the source code before you contact us
 *     about purchasing a commercial license.
 *
 *     LEGAL TERMS : "CloudUnit" is a registered trademark of Treeptik and can't be used to endorse
 *     or promote products derived from this project without prior written permission from Treeptik.
 *     Products or services derived from this software may not be called "CloudUnit"
 *     nor may "Treeptik" or similar confusing terms appear in their names without prior written permission.
 *     For any questions, contact us : contact@treeptik.fr
 */

package fr.treeptik.cloudunit.modules;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mongodb.MongoClient;
import fr.treeptik.cloudunit.dto.EnvUnit;
import fr.treeptik.cloudunit.dto.ModulePortResource;
import fr.treeptik.cloudunit.exception.ServiceException;
import fr.treeptik.cloudunit.initializer.CloudUnitApplicationContext;
import fr.treeptik.cloudunit.model.User;
import fr.treeptik.cloudunit.service.UserService;
import fr.treeptik.cloudunit.utils.CheckBrokerConnectionUtils;
import fr.treeptik.cloudunit.utils.SpyMatcherDecorator;
import fr.treeptik.cloudunit.utils.TestUtils;
import junit.framework.TestCase;
import org.apache.commons.io.FilenameUtils;
import org.junit.*;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.exceptions.JedisConnectionException;

import javax.inject.Inject;
import javax.servlet.Filter;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.UnknownHostException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import static org.awaitility.Awaitility.await;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

/**
 * Tests for Module lifecycle
 */
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = { CloudUnitApplicationContext.class, MockServletContext.class })
@ActiveProfiles("integration")
public abstract class AbstractModuleControllerTestIT extends TestCase {

    protected final Logger logger = LoggerFactory.getLogger(AbstractModuleControllerTestIT.class);

    @Autowired
    protected WebApplicationContext context;

    protected MockMvc mockMvc;

    @Inject
    private ObjectMapper objectMapper;

    @Inject
    private AuthenticationManager authenticationManager;

    @Autowired
    private Filter springSecurityFilterChain;

    @Inject
    private UserService userService;

    @Value("${cloudunit.instance.name}")
    private String cuInstanceName;

    @Value("${ip.box.vagrant}")
    protected String ipVagrantBox;

    protected MockHttpSession session;

    protected static String applicationName;

    @Value("${suffix.cloudunit.io}")
    private String domainSuffix;

    @Value("#{systemEnvironment['CU_SUB_DOMAIN']}")
    private String subdomain;

    @Inject
    private CheckBrokerConnectionUtils checkBrokerConnectionUtils;

    protected String server;
    protected String module;
    protected String numberPort;
    protected String managerPrefix;
    protected String managerSuffix;
    protected String managerPageContent;
    protected String testScriptPath;

    @BeforeClass
    public static void initEnv() {
        applicationName = "app" + new Random().nextInt(100000);
    }

    @Before
    public void setup() throws Exception {
        logger.info("setup");

        this.mockMvc = MockMvcBuilders.webAppContextSetup(context).addFilters(springSecurityFilterChain).build();

        User user = null;
        try {
            user = userService.findByLogin("johndoe");
        } catch (ServiceException e) {
            logger.error(e.getLocalizedMessage());
        }

        assert user != null;
        Authentication authentication = new UsernamePasswordAuthenticationToken(user.getLogin(),
                user.getPassword());
        Authentication result = authenticationManager.authenticate(authentication);
        SecurityContext securityContext = SecurityContextHolder.getContext();
        securityContext.setAuthentication(result);
        session = new MockHttpSession();
        String secContextAttr = HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;
        session.setAttribute(secContextAttr, securityContext);

        // create an application server
        String jsonString = "{\"applicationName\":\"" + applicationName + "\", \"serverName\":\"" + server + "\"}";
        mockMvc.perform(
                post("/application").session(session).contentType(MediaType.APPLICATION_JSON).content(jsonString))
                .andExpect(status().isOk());
    }

    @After
    public void teardown() throws Exception {
        logger.info("teardown");

        logger.info("Delete application : " + applicationName);

        mockMvc.perform(
                delete("/application/" + applicationName).session(session).contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk());

        SecurityContextHolder.clearContext();
        session.invalidate();
    }

    @Test
    public void test00_FailToAddModuleBecauseBadAppName() throws Exception {
        logger.info("Cannot add a module because application name missing");
        String jsonString = "{\"applicationName\":\"" + "" + "\", \"imageName\":\"" + module + "\"}";
        ResultActions resultats = mockMvc.perform(
                post("/module").session(session).contentType(MediaType.APPLICATION_JSON).content(jsonString));
        resultats.andExpect(status().is4xxClientError());
    }

    @Test
    public void test01_FailToAddModuleBecauseAppNonExist() throws Exception {
        logger.info("Cannot add a module because application name missing");
        String jsonString = "{\"applicationName\":\"" + "UFO" + "\", \"imageName\":\"" + module + "\"}";
        ResultActions resultats = mockMvc.perform(
                post("/module").session(session).contentType(MediaType.APPLICATION_JSON).content(jsonString));
        resultats.andExpect(status().is4xxClientError());
    }

    @Test
    public void test02_FailToAddModuleBecauseModuleEmpty() throws Exception {
        logger.info("Cannot add a module because module name empty");
        String jsonString = "{\"applicationName\":\"" + "REALAPP" + "\", \"imageName\":\"" + "" + "\"}";
        ResultActions resultats = mockMvc.perform(
                post("/module").session(session).contentType(MediaType.APPLICATION_JSON).content(jsonString));
        resultats.andExpect(status().is4xxClientError());
    }

    @Test
    public void test03_FailToAddModuleBecauseModuleNonExisting() throws Exception {
        logger.info("Cannot add a module because module name empty");
        String jsonString = "{\"applicationName\":\"" + "REALAPP" + "\", \"imageName\":\"" + "UFO" + "\"}";
        ResultActions resultats = mockMvc.perform(
                post("/module").session(session).contentType(MediaType.APPLICATION_JSON).content(jsonString));
        resultats.andExpect(status().is4xxClientError());
    }

    @Test
    public void test10_CreateServerThenAddModule() throws Exception {
        logger.info("Create an application, add a " + module + " module and delete it");

        // verify if app exists
        ResultActions resultats = mockMvc.perform(
                get("/application/" + applicationName).session(session).contentType(MediaType.APPLICATION_JSON));
        resultats.andExpect(jsonPath("name").value(applicationName.toLowerCase()));

        // add a module
        String jsonString = "{\"applicationName\":\"" + applicationName + "\", \"imageName\":\"" + module + "\"}";
        resultats = mockMvc.perform(
                post("/module").session(session).contentType(MediaType.APPLICATION_JSON).content(jsonString));
        resultats.andExpect(status().isOk());

        // Expected values
        String genericModule = cuInstanceName.toLowerCase() + "-johndoe-" + applicationName.toLowerCase() + "-"
                + module;

        // get the detail of the applications to verify modules addition
        resultats = mockMvc.perform(
                get("/application/" + applicationName).session(session).contentType(MediaType.APPLICATION_JSON))
                .andDo(print());
        resultats.andExpect(status().isOk()).andExpect(jsonPath("$.modules[0].status").value("START"))
                .andExpect(jsonPath("$.modules[0].name").value(genericModule));

        // remove a module
        resultats = mockMvc.perform(delete("/module/" + applicationName + "/" + genericModule).session(session)
                .contentType(MediaType.APPLICATION_JSON));
        resultats.andExpect(status().isOk());

        // get the detail of the applications to verify modules addition
        resultats = mockMvc.perform(
                get("/application/" + applicationName).session(session).contentType(MediaType.APPLICATION_JSON))
                .andDo(print());
        resultats.andExpect(status().isOk()).andExpect(jsonPath("$.modules[0]").doesNotExist());
    }

    @Test
    public void test20_FailCreateServerThenAddTwoModule() throws Exception {
        logger.info("Create an application, add two " + module + " modules, stop them then delete all");

        // verify if app exists
        ResultActions resultats = mockMvc.perform(
                get("/application/" + applicationName).session(session).contentType(MediaType.APPLICATION_JSON));
        resultats.andExpect(jsonPath("name").value(applicationName.toLowerCase()));

        // add a first module
        String jsonString = "{\"applicationName\":\"" + applicationName + "\", \"imageName\":\"" + module + "\"}";
        resultats = mockMvc.perform(
                post("/module").session(session).contentType(MediaType.APPLICATION_JSON).content(jsonString));
        resultats.andExpect(status().isOk());

        // Expected values
        String module1 = cuInstanceName.toLowerCase() + "-johndoe-" + applicationName.toLowerCase() + "-" + module;

        // get the detail of the applications to verify modules addition
        resultats = mockMvc.perform(
                get("/application/" + applicationName).session(session).contentType(MediaType.APPLICATION_JSON))
                .andDo(print());
        resultats.andExpect(status().isOk()).andExpect(jsonPath("$.modules[0].status").value("START"))
                .andExpect(jsonPath("$.modules[0].name").value(module1));

        // add a second module
        jsonString = "{\"applicationName\":\"" + applicationName + "\", \"imageName\":\"" + module + "\"}";
        resultats = mockMvc.perform(
                post("/module").session(session).contentType(MediaType.APPLICATION_JSON).content(jsonString));
        resultats.andExpect(status().is4xxClientError());
    }

    @Test
    public void test30_AddModuleThenRestart() throws Exception {
        logger.info("Create an application, add a " + module + " modules, restart");

        // verify if app exists
        ResultActions resultats = mockMvc.perform(
                get("/application/" + applicationName).session(session).contentType(MediaType.APPLICATION_JSON));
        resultats.andExpect(jsonPath("name").value(applicationName.toLowerCase()));

        // add a first module
        String jsonString = "{\"applicationName\":\"" + applicationName + "\", \"imageName\":\"" + module + "\"}";
        resultats = mockMvc.perform(
                post("/module").session(session).contentType(MediaType.APPLICATION_JSON).content(jsonString));
        resultats.andDo(print());
        resultats.andExpect(status().isOk());

        // Expected values
        String module1 = cuInstanceName.toLowerCase() + "-johndoe-" + applicationName.toLowerCase() + "-" + module;

        // Stop the application
        jsonString = "{\"applicationName\":\"" + applicationName + "\"}";
        resultats = mockMvc.perform(post("/application/stop").session(session)
                .contentType(MediaType.APPLICATION_JSON).content(jsonString));
        resultats.andDo(print());
        resultats.andExpect(status().isOk());

        // Start the application
        jsonString = "{\"applicationName\":\"" + applicationName + "\"}";
        resultats = mockMvc.perform(post("/application/start").session(session)
                .contentType(MediaType.APPLICATION_JSON).content(jsonString));
        resultats.andDo(print());
        resultats.andExpect(status().isOk());

        // get the detail of the applications to verify modules addition
        resultats = mockMvc.perform(
                get("/application/" + applicationName).session(session).contentType(MediaType.APPLICATION_JSON))
                .andDo(print());
        resultats.andDo(print());
        resultats.andExpect(status().isOk()).andExpect(jsonPath("$.modules[0].status").value("START"))
                .andExpect(jsonPath("$.modules[0].name").value(module1));
    }

    @Test
    public void test_PublishPort() throws Exception {
        logger.info("Publish module port for external access");

        requestAddModule().andExpect(status().isOk());

        SpyMatcherDecorator<Integer> responseModuleIdSpy = new SpyMatcherDecorator<>(Integer.class);
        SpyMatcherDecorator<List> forwardedPort = new SpyMatcherDecorator<>(List.class);

        requestApplication().andExpect(status().isOk()).andExpect(jsonPath("$.modules[0].id", responseModuleIdSpy));

        Integer moduleId = responseModuleIdSpy.getMatchedValue();
        requestPublishPort(moduleId, numberPort).andExpect(status().isOk());

        requestApplication().andExpect(status().isOk()).andDo(print());
        requestApplication().andExpect(status().isOk()).andDo(print())
                .andExpect(jsonPath(
                        String.format("$.modules[0].ports[?(@.containerValue == %s)].hostValue", numberPort),
                        forwardedPort));
        checkConnection(forwardedPort.getMatchedValue().stream().findFirst().get().toString());
    }

    protected abstract void checkConnection(String forwardedPort);

    private String getContainerName() {
        return "int-johndoe-" + applicationName + "-" + module;
    }

    @Test
    public void test_runScript() throws Exception {
        requestAddModule();
        String filename = FilenameUtils.getName(testScriptPath);
        if (filename == null) {
            logger.info("No script found - test escape");
        } else {
            MockMultipartFile file = new MockMultipartFile("file", filename, "application/sql",
                    new FileInputStream(testScriptPath));

            String genericModule = cuInstanceName.toLowerCase() + "-johndoe-" + applicationName.toLowerCase() + "-"
                    + module;

            ResultActions result = mockMvc.perform(fileUpload("/module/{moduleName}/run-script", genericModule)
                    .file(file).session(session).contentType(MediaType.MULTIPART_FORM_DATA)).andDo(print());
            result.andExpect(status().isOk());
        }
    }

    /**
     * Inner class to check relational database connection
     *
     */
    public class CheckDatabaseConnection {

        public void invoke(String forwardedPort, String keyUser, String keyPassword, String keyDB, String driver,
                String jdbcUrlPrefix) {
            try {
                String urlToCall = "/application/" + applicationName + "/container/" + getContainerName() + "/env";
                ResultActions resultats = mockMvc
                        .perform(get(urlToCall).session(session).contentType(MediaType.APPLICATION_JSON));
                String contentResult = resultats.andReturn().getResponse().getContentAsString();
                List<EnvUnit> envs = objectMapper.readValue(contentResult, new TypeReference<List<EnvUnit>>() {
                });
                final String user = envs.stream().filter(e -> e.getKey().equals(keyUser)).findFirst()
                        .orElseThrow(() -> new RuntimeException("Missing " + keyUser)).getValue();
                final String password = envs.stream().filter(e -> e.getKey().equals(keyPassword)).findFirst()
                        .orElseThrow(() -> new RuntimeException("Missing " + keyPassword)).getValue();
                final String database = envs.stream().filter(e -> e.getKey().equals(keyDB)).findFirst()
                        .orElseThrow(() -> new RuntimeException("Missing " + keyDB)).getValue();
                final String jdbcUrl = jdbcUrlPrefix + ipVagrantBox + ":" + forwardedPort + "/" + database;
                Class.forName(driver);
                await("Testing database connection...").atMost(5, TimeUnit.SECONDS).and().ignoreExceptions()
                        .until(() -> {
                            try (final Connection connection = DriverManager.getConnection(jdbcUrl, user,
                                    password)) {
                                return connection.isValid(1000);
                            }
                        });
            } catch (Exception e) {
                e.printStackTrace();
                Assert.fail();
            }
        }
    }

    /**
     * Inner class to check message broker
     *
     */
    public class CheckBrokerConnection {
        public void invoke(String forwardedPort, String keyUser, String keyPassword, String keyDB,
                String protocol) {
            String user = null;
            String password = null;
            String vhost = null;
            String urlToCall = "/application/" + applicationName + "/container/" + getContainerName() + "/env";
            String contentResult = null;
            try {
                ResultActions resultats = mockMvc
                        .perform(get(urlToCall).session(session).contentType(MediaType.APPLICATION_JSON));
                contentResult = resultats.andReturn().getResponse().getContentAsString();
                List<EnvUnit> envs = objectMapper.readValue(contentResult, new TypeReference<List<EnvUnit>>() {
                });
                contentResult = resultats.andReturn().getResponse().getContentAsString();
                user = envs.stream().filter(e -> e.getKey().equals(keyUser)).findFirst()
                        .orElseThrow(() -> new RuntimeException("Missing " + keyUser)).getValue();
                password = envs.stream().filter(e -> e.getKey().equals(keyPassword)).findFirst()
                        .orElseThrow(() -> new RuntimeException("Missing " + keyPassword)).getValue();
                vhost = envs.stream().filter(e -> e.getKey().equals(keyDB)).findFirst()
                        .orElseThrow(() -> new RuntimeException("Missing " + keyDB)).getValue();
                String brokerURL = ipVagrantBox + ":" + forwardedPort;
                String message = "Hello world!";

                switch (protocol) {
                case "JMS":
                    Assert.assertEquals(message,
                            checkBrokerConnectionUtils.checkActiveMQJMSProtocol(message, brokerURL));
                    break;
                case "AMQP":
                    Assert.assertEquals(message, checkBrokerConnectionUtils.checkRabbitMQAMQPProtocol(message,
                            brokerURL, user, password, vhost));
                    break;
                default:
                    throw new RuntimeException("Protocol " + keyDB + " not supported yet");
                }

            } catch (Exception e) {
                e.printStackTrace();
                Assert.fail();
            }

        }
    }

    /**
     * Inner class to check elasticsearch connection
     *
     */
    public class CheckElasticSearchConnection {
        public void invoke(String forwardedPort) {
            String url = String.format("http://%s:%s", ipVagrantBox, forwardedPort);
            try {
                await("Testing database connection...").atMost(5, TimeUnit.SECONDS).and().ignoreExceptions()
                        .until(() -> {
                            try {

                                "elasticsearch".contains(TestUtils.getUrlContentPage(url));
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        });
            } catch (Exception e) {
                Assert.fail();
                e.printStackTrace();
            }
        }
    }

    /**
     * Inner class to check redis connection
     *
     */
    public class CheckRedisConnection {

        public void invoke(String forwardedPort) {
            try (JedisPool pool = new JedisPool(new JedisPoolConfig(), ipVagrantBox,
                    Integer.parseInt(forwardedPort), 3000)) {
                Jedis jedis = pool.getResource();
            } catch (JedisConnectionException e) {
                Assert.fail();
                e.printStackTrace();
            }
        }
    }

    /**
     * Inner class to check redis connection
     *
     */
    public class CheckMongoConnection {
        public void invoke(String forwardedPort) {
            MongoClient mongo = null;
            try {
                mongo = new MongoClient(ipVagrantBox, Integer.parseInt(forwardedPort));
            } catch (UnknownHostException e) {
                Assert.fail();
                e.printStackTrace();
            } finally {
                mongo.close();
            }
        }
    }

    private ResultActions requestPublishPort(Integer id, String number) throws Exception {
        ModulePortResource request = ModulePortResource.of().withPublishPort(true).build();
        String jsonString = objectMapper.writeValueAsString(request);
        return mockMvc.perform(put("/module/" + id + "/ports/" + number).session(session)
                .contentType(MediaType.APPLICATION_JSON).content(jsonString)).andDo(print());
    }

    private ResultActions requestAddModule() throws Exception {
        String jsonString = "{\"applicationName\":\"" + applicationName + "\", \"imageName\":\"" + module + "\"}";
        return mockMvc.perform(
                post("/module").session(session).contentType(MediaType.APPLICATION_JSON).content(jsonString))
                .andDo(print());
    }

    private ResultActions requestApplication() throws Exception {
        return mockMvc.perform(
                get("/application/" + applicationName).session(session).contentType(MediaType.APPLICATION_JSON))
                .andDo(print());
    }

}