io.github.retz.web.WebConsoleCommonTests.java Source code

Java tutorial

Introduction

Here is the source code for io.github.retz.web.WebConsoleCommonTests.java

Source

/**
 *    Retz
 *    Copyright (C) 2016-2017 Nautilus Technologies, Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package io.github.retz.web;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import io.github.retz.cli.ClientCLIConfig;
import io.github.retz.cli.TimestampHelper;
import io.github.retz.db.Database;
import io.github.retz.misc.ApplicationBuilder;
import io.github.retz.protocol.*;
import io.github.retz.protocol.data.*;
import io.github.retz.scheduler.*;
import org.apache.mesos.Protos;
import org.hamcrest.Matchers;
import org.junit.*;

import java.io.IOException;
import java.util.*;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static spark.Spark.awaitInitialization;

// These tests must pass regardless of any client/server communication configuration
@Ignore
public class WebConsoleCommonTests {
    private final List<String> BASE_ORDER_BY = Arrays.asList("id");
    private Client webClient;
    private ObjectMapper mapper;
    private ServerConfiguration config;
    private ClientCLIConfig cliConfig;

    Launcher.Configuration makeConfig() throws Exception {
        throw new RuntimeException("This class shouldn't be tested");
    }

    ClientCLIConfig makeClientConfig() throws Exception {
        throw new RuntimeException("This class shouldn't be tested");
    }

    @Before
    public void setUp() throws Exception {

        Protos.FrameworkInfo frameworkInfo = Protos.FrameworkInfo.newBuilder().setUser("")
                .setName(RetzScheduler.FRAMEWORK_NAME).build();

        // Non-TLS tests are not to be done, I believe when it works with TLS tests, it should work
        // on Non-TLS setup too. I believe his is because Sparkjava does not cleanly clear TLS setting in
        // Spark.stop(), because with retz.properties it succeeds alone, but fails when right after TLS tests.
        // TODO: investigate and report this to sparkjava
        Launcher.Configuration conf = makeConfig();

        mapper = new ObjectMapper();
        mapper.registerModule(new Jdk8Module());

        RetzScheduler scheduler = new RetzScheduler(conf, frameworkInfo);
        config = conf.getServerConfig();
        Database.getInstance().init(config);
        assertTrue(Database.getInstance().allTableExists());

        WebConsole.set(scheduler, null);
        WebConsole.start(config);
        awaitInitialization();

        cliConfig = makeClientConfig();
        System.err.println(config.authenticationEnabled());
        System.err.println(config.toString());
        webClient = Client.newBuilder(cliConfig.getUri()).setAuthenticator(cliConfig.getAuthenticator())
                .checkCert(!cliConfig.insecure()).setVerboseLog(true).build();
    }

    @After
    public void tearDown() throws Exception {
        webClient.close();
        WebConsole.stop();
        Database.getInstance().clear();
        Database.getInstance().stop();
        // This is because Spark.stop() is impelemented in asynchronous way,
        // there are no way to wait for spark.Service.initialized become false.
        // If this sleep is too short, IllegalStateException("This must be done before route mapping has begun");
        // will be thrown.
        Thread.sleep(1024);
    }

    @Test
    public void version() {
        System.err.println(Client.VERSION_STRING);
    }

    @Test
    public void list() throws Exception {
        {
            ListJobResponse res = (ListJobResponse) webClient.list(Job.JobState.QUEUED, Optional.empty());
            assertTrue(res.jobs().isEmpty());
            assertFalse(res.more());
        }
        {
            ListJobResponse res = (ListJobResponse) webClient.list(Job.JobState.STARTING, Optional.empty());
            assertTrue(res.jobs().isEmpty());
            assertFalse(res.more());
        }
        {
            ListJobResponse res = (ListJobResponse) webClient.list(Job.JobState.FINISHED, Optional.empty());
            assertTrue(res.jobs().isEmpty());
            assertFalse(res.more());
        }
    }

    @Test
    public void loadApp() throws Exception {
        {
            String[] files = { "http://example.com:234/foobar/test.tar.gz" };
            Application app = new Application("foobar", new LinkedList<String>(), Arrays.asList(files),
                    Optional.empty(), config.getUser().keyId(), 0, new MesosContainer(), true);
            Response res = webClient.load(app);
            assertThat(res, instanceOf(LoadAppResponse.class));
            assertThat(res.status(), Matchers.is("ok"));

            Optional<Application> app2 = Applications.get(app.getAppid());
            assertTrue(app2.isPresent());
            assertThat(app2.get().getAppid(), is(app.getAppid()));
        }

        {
            GetAppResponse res = (GetAppResponse) webClient.getApp("foobar");
            assertThat(res.status(), is("ok"));
            Application app = res.application();
            assertThat(app.getAppid(), is("foobar"));
        }
        webClient.unload("foobar");
    }

    @Test
    public void schedule() throws Exception {
        List<Job> maybeJob = JobQueue.findFit(BASE_ORDER_BY, new ResourceQuantity(10000, 10000, 0, 0, 0, 0));
        assertTrue(maybeJob.isEmpty());

        {
            // Job request without app must fail
            String cmd = "Mmmmmmmmmy commmmmand1!!!!!";
            Response res = webClient.schedule(new Job("foobar", cmd, null, 1, 256));
            assertThat(res, instanceOf(ErrorResponse.class));

            maybeJob = JobQueue.findFit(BASE_ORDER_BY, new ResourceQuantity(1000, 10000, 0, 0, 0, 0));
            assertTrue(maybeJob.isEmpty());

            GetJobResponse getJobResponse = (GetJobResponse) webClient.getJob(235561234);
            assertFalse(getJobResponse.job().isPresent());
        }

        {
            String[] files = { "http://example.com:234/foobar/test.tar.gz" };
            Application app = new Application("foobar", new LinkedList<>(), Arrays.asList(files), Optional.empty(),
                    config.getUser().keyId(), 0, new MesosContainer(), true);
            Response res = webClient.load(app);
            System.err.println(">>>" + res.status());
            assertThat(res, instanceOf(LoadAppResponse.class));
            assertThat(res.status(), is("ok"));

            Optional<Application> app2 = Applications.get("foobar");
            assertTrue(app2.isPresent());
            assertThat(app2.get().getAppid(), is("foobar"));

            res = webClient.getApp("foobar");
            assertThat(res, instanceOf(GetAppResponse.class));
            GetAppResponse getAppResponse = (GetAppResponse) res;
            assertThat(getAppResponse.application().getAppid(), is("foobar"));
            assertThat(getAppResponse.application().getFiles().size(), is(1));
        }
        maybeJob = JobQueue.findFit(BASE_ORDER_BY, new ResourceQuantity(10000, 10000, 0, 0, 0, 0));
        assertTrue(maybeJob.isEmpty());

        {
            // You know, these spaces are to be normalized
            String cmd = "Mmmmmmmmmy commmmmand1!!!!!";
            Response res = webClient.schedule(new Job("foobar", cmd, null, 1, 200));
            assertThat(res, instanceOf(ScheduleResponse.class));
            ScheduleResponse sres = (ScheduleResponse) res;
            assertNotNull(sres.job.scheduled());
            assertThat(sres.job.id(), is(greaterThanOrEqualTo(0)));
            System.err.println(sres.job.scheduled());

            assertThat(ClientHelper.queue(webClient).size(), is(1));

            GetJobResponse getJobResponse = (GetJobResponse) webClient.getJob(sres.job.id());
            Assert.assertEquals(sres.job.cmd(), getJobResponse.job().get().cmd());

            maybeJob = JobQueue.findFit(BASE_ORDER_BY, new ResourceQuantity(10000, 10000, 0, 0, 0, 0));
            assertFalse(maybeJob.isEmpty());
            assertThat(maybeJob.get(0).cmd(), is(cmd));
            assertThat(maybeJob.get(0).appid(), is("foobar"));

            ListFilesResponse listFilesResponse = (ListFilesResponse) webClient.listFiles(sres.job.id(),
                    ListFilesRequest.DEFAULT_SANDBOX_PATH);
            assertTrue(listFilesResponse.entries().isEmpty());

            GetFileResponse getFileResponse = (GetFileResponse) webClient.getFile(sres.job.id(), "stdout", 0,
                    20000);
            assertFalse(getFileResponse.file().isPresent());
        }

        {
            Response res = webClient.getApp("no such app");
            assertThat(res, instanceOf(ErrorResponse.class));
        }
        webClient.unload("foobar");
    }

    @Test
    public void runFail() throws Exception {
        JobQueue.clear();
        List<Job> maybeJob = JobQueue.findFit(BASE_ORDER_BY, new ResourceQuantity(10000, 10000, 0, 0, 0, 0));
        assertTrue(maybeJob.isEmpty());

        {
            // Job request without app must fail
            String cmd = "Mmmmmmmmmy commmmmand1!!!!!";
            Job job = new Job("foobar-nosuchapp", cmd, null, 1, 256);
            Job done = webClient.run(job);
            assertNull(done);

            maybeJob = JobQueue.findFit(BASE_ORDER_BY, new ResourceQuantity(10000, 10000, 0, 0, 0, 0));
            assertTrue(maybeJob.isEmpty());
        }
    }

    @Test
    public void kill() throws Exception {
        {
            Response res = webClient.kill(0);
            assertThat(res, instanceOf(ErrorResponse.class));
            assertEquals("No such job: 0", res.status());
        }
        Application app = new ApplicationBuilder("app", config.getUser().keyId()).build();
        LoadAppResponse loadAppResponse = (LoadAppResponse) webClient.load(app);
        assertEquals("ok", loadAppResponse.status());
        {
            Job job = new Job("app", "sleep 1000", new Properties(), 1, 64);
            ScheduleResponse scheduleResponse = (ScheduleResponse) webClient.schedule(job);
            KillResponse killResponse = (KillResponse) webClient.kill(scheduleResponse.job().id());
            assertEquals("ok", killResponse.status());
            GetJobResponse getJobResponse = (GetJobResponse) webClient.getJob(scheduleResponse.job().id());
            System.err.println(getJobResponse.job().get().pp());
            assertEquals(Job.JobState.KILLED, getJobResponse.job().get().state());
        }
    }

    @Test
    public void ping() throws IOException {
        Client c = Client.newBuilder(config.getUri()).setAuthenticator(config.getAuthenticator())
                .checkCert(!config.insecure()).build();
        assertTrue(c.ping());
    }

    @Test
    public void status() throws Exception {
        Application app = new Application("fooapp", Arrays.asList(), Arrays.asList(), Optional.empty(),
                config.getUser().keyId(), 0, new MesosContainer(), true);
        Database.getInstance().addApplication(app);
        Job job = new Job(app.getAppid(), "foocmd", null, 12000, 12000);
        job.schedule(JobQueue.issueJobId(), TimestampHelper.now());
        JobQueue.push(job);
        StatusCache.updateUsedResources();

        try (Client c = Client.newBuilder(config.getUri()).setAuthenticator(config.getAuthenticator())
                .checkCert(!config.insecure()).build()) {
            Response res = c.status();
            assertThat(res, instanceOf(StatusResponse.class));
            StatusResponse statusResponse = (StatusResponse) res;

            System.err.println(statusResponse.queueLength());
            assertThat(statusResponse.queueLength(), is(1));
            assertThat(statusResponse.runningLength(), is(0));
        }
    }

    // Checks isolation between users.
    // All combination of client APIs must be safely excluded,
    // Any request to server imitation other users must fail.
    @Test
    public void isolation() throws Exception {
        // Prepare data
        Application app1 = new Application("app1", Arrays.asList(), Arrays.asList(), Optional.empty(),
                cliConfig.getUser().keyId(), 0, new MesosContainer(), true);
        Job job1 = new Job("app1", "ls", new Properties(), 1, 32);

        {
            Response res = webClient.load(app1);
            assertEquals("ok", res.status());
        }
        {
            Response res = webClient.schedule(job1);
            assertEquals("ok", res.status());
            ScheduleResponse scheduleResponse = (ScheduleResponse) res;
            job1 = scheduleResponse.job();
        }
        System.err.println("Job " + job1.id() + " has been scheduled");

        // Here comes a new challenger!!
        User charlie = new User("charlie", "snoops!", true, "Charlie the theif");
        Database.getInstance().addUser(charlie);

        ClientCLIConfig c2 = new ClientCLIConfig(cliConfig);
        c2.setUser(charlie);

        assertEquals("deadbeef", cliConfig.getUser().keyId());
        assertEquals("charlie", c2.getUser().keyId());

        try (Client client2 = Client.newBuilder(c2.getUri()).setAuthenticator(c2.getAuthenticator())
                .checkCert(!c2.insecure()).setVerboseLog(true).build()) {

            {
                Response res = client2.listApp();
                assertEquals("ok", res.status());
                ListAppResponse listAppResponse = (ListAppResponse) res;
                assertTrue(listAppResponse.applicationList().isEmpty());
            }
            {
                Response res = client2.getApp("app1");
                assertThat(res, instanceOf(ErrorResponse.class));
            }
            {
                assertTrue(ClientHelper.finished(client2).isEmpty());
                assertTrue(ClientHelper.queue(client2).isEmpty());
                assertTrue(ClientHelper.running(client2).isEmpty());

            }
            { // Charlie tries to snoop Job info of Alice
                Response res = client2.getJob(job1.id());
                assertThat(res, instanceOf(GetJobResponse.class));
                GetJobResponse getJobResponse = (GetJobResponse) res;
                assertFalse(getJobResponse.job().isPresent());
                System.err.println(res.status());
            }
            { // Charlie tries to kill Alice's job
                Response res = client2.kill(job1.id());
                assertThat(res.status(), not(is("ok")));
                assertThat(res, instanceOf(ErrorResponse.class));

                GetJobResponse getJobResponse = (GetJobResponse) webClient.getJob(job1.id());
                assertThat(getJobResponse.job().get().state(), is(Job.JobState.QUEUED));
            }
            { // Charlie tries to snoop files in Alice's job sandbox
                Response res = client2.getFile(job1.id(), "stdout", 0, -1);
                assertThat(res, instanceOf(GetFileResponse.class));
                GetFileResponse getFileResponse = (GetFileResponse) res;
                assertFalse(getFileResponse.job().isPresent());
                assertFalse(getFileResponse.file().isPresent());
                System.err.println(res.status());
            }
            { // Charlie tries to snoop files in Alice's job sandbox
                Response res = client2.listFiles(job1.id(), ListFilesRequest.DEFAULT_SANDBOX_PATH);
                assertThat(res, instanceOf(ListFilesResponse.class));
                ListFilesResponse listFilesResponse = (ListFilesResponse) res;
                assertFalse(listFilesResponse.job().isPresent());
                assertTrue(listFilesResponse.entries().isEmpty());
                System.err.println(res.status());
            }
            {
                // Charlie tries to steal Alice's whole application
                Response res = client2.load(app1);
                assertThat(res, instanceOf(ErrorResponse.class));
                System.err.println(res.status());
            }
            { // Charlie tries to steal Alice's application name
                Application app2 = new Application("app1", Arrays.asList(), Arrays.asList(), Optional.empty(),
                        c2.getUser().keyId(), 0, new MesosContainer(), true);
                Response res = client2.load(app2);
                assertThat(res, instanceOf(ErrorResponse.class));
                System.err.println(res.status());
            }
            { // Charlie tries to be Alice
                System.err.println(cliConfig.getUser().keyId());
                Application app2 = new Application("app2", Arrays.asList(), Arrays.asList(), Optional.empty(),
                        cliConfig.getUser().keyId(), 0, new MesosContainer(), true);
                Response res = client2.load(app2);
                assertThat(res, instanceOf(ErrorResponse.class));
                System.err.println(res.status());
            }

            Job job2 = new Job("app1", "ls", new Properties(), 1, 32);
            { // Charlie tries to steal Alice's applications
                Response res = client2.schedule(job2);
                assertThat(res, instanceOf(ErrorResponse.class));
                System.err.println(res.status());
            }
            { // Charlie tries to steal Alice's applications
                Job job3 = client2.run(job2);
                assertEquals(null, job3);
            }
        }
    }

    @Test
    public void disableUser() throws Exception {
        User user = config.getUser();
        List<String> e = Arrays.asList();
        Application application = new Application("t", e, e, Optional.empty(), user.keyId(), 0,
                new MesosContainer(), true);

        String jar = "/build/libs/retz-admin-all.jar";
        String cfg = "/retz-persistent.properties";

        Client client = webClient;
        Response res;

        res = client.load(application);
        assertEquals("ok", res.status());

        res = client.schedule(new Job("t", "ls", new Properties(), 1, 32));
        ScheduleResponse scheduleResponse = (ScheduleResponse) res;
        Job job1 = scheduleResponse.job();

        System.err.println("Disable user " + user.keyId());
        Database.getInstance().enableUser(user.keyId(), false);
        {
            Optional<User> u = Database.getInstance().getUser(user.keyId());
            assertFalse(u.get().enabled());
        }

        res = client.getJob(job1.id());
        System.err.println(res.status());
        Assert.assertThat(res, instanceOf(ErrorResponse.class));

        res = client
                .load(new Application("t2", e, e, Optional.empty(), user.keyId(), 0, new MesosContainer(), true));
        System.err.println(res.status());
        Assert.assertThat(res, instanceOf(ErrorResponse.class));

        res = client.schedule(new Job("t", "echo prohibited job", new Properties(), 1, 32));
        System.err.println(res.status());
        Assert.assertThat(res, instanceOf(ErrorResponse.class));

        System.err.println("Enable user");
        Database.getInstance().enableUser(user.keyId(), true);

        res = client.getJob(job1.id());
        System.err.println(res.status());
        assertEquals("ok", res.status());

        res = client
                .load(new Application("t2", e, e, Optional.empty(), user.keyId(), 0, new MesosContainer(), true));
        System.err.println(res.status());
        assertEquals("ok", res.status());

        res = client.schedule(new Job("t", "echo okay job", new Properties(), 1, 32));
        System.err.println(res.status());
        assertEquals("ok", res.status());
    }
}