Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added auto book feature #2

Merged
merged 5 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion cradle-admin-tool-http/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# cradle-admin-tool-http (1.7.2)
# cradle-admin-tool-http (1.8.0)
Service which allows user to manage books/pages via RestAPI requests.
- The first page in a book can be created only if start time is more than current time.
- After the first page all new pages must have start time more than current time + `bookRefreshIntervalMillis` * 2
Expand All @@ -12,6 +12,10 @@ Service which allows user to manage books/pages via RestAPI requests.
- **ip** - host where http cradle admin instance will be instanciated. Default value: `0.0.0.0`
- **port** - port on which http server will listen user requests. Default value: `8080`
- **page-recheck-interval** - interval in seconds which `PageManager` service checks if new page is required to create or not based on duration values presented in `auto-pages`. Default value: 60 seconds
- **auto-books** - defines rule for automatic books creation. If empty no books will be created automatically. Default value: `empty_map`.
- **book-creation-time** - book creation time. Default value is current time.
- **book-full-name** - book full name. Default value is book name
- **book-description** - book description. Default value is `auto-book`
- **auto-pages** - defines rule for automatic pages creation for multiple books. If empty no pages will be created automatically. Default value: `empty_map`.
- **page-duration** - defines duration of the page for the book. Value uses the Java Duration format. You can read more about it [here](https://docs.oracle.com/javase/8/docsT/api/java/time/Duration.html#parse-java.lang.CharSequence-).
- **page-start-time** - baseline date and time for every new page created by `PageManager` for this book.
Expand All @@ -29,6 +33,17 @@ spec:
custom-config:
ip: 198.168.0.2
port: 8080
auto-book:
book1:
book-creation-time: 2023-03-27T12:00:00
book-full-name: book1-full-name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that we don't need to provide the ability to specify the exact time for a book. This is auto functionality. So, I think it just should create a book with the current time (if the book does not exist).
A user always can call the endpoint to create a book with the time he or she needs.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, it sound like correct but I think admin-tool should create book with time now - 1 hour and create a first page with the same date because schema can contain other active conns which can be started before admin-tool.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then maybe this is the problem in the schema. The cradle admin should be deployed before other components. Also, you always can create a book in the past using REST API in case there are events/messages from components that were deployed before cradle-admin...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then maybe this is the problem in the schema. I don't think so, because by the th2 concept order of component starting doesn't matter.
REST API is complicated way when you need the simplest book to start work.
I try to solve start on a virgin machine problem by this change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, because by the th2 concept order of component starting doesn't matter.
We have a problem then because the order does matter)) What if the cradle-admin will be started after 1 hour + 1 second after the first component produces event/message? We still will have the problem that there is no book that matches the event/message time.
We don't make our and users' lives easier by saying let's just substruct an hour or please, set the exact time you need for the book in config. The only solution to the problem is to start cradle-admin before other components. Otherwise, we are doomed to face that potential problem again and again.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your statement is true when CR cradle-admin doesn't exist at the start environment moment or it has critical problem in configuration and user doesn't check environment.
In fact, user can have a problem event infra will start cradle-admin at first. We can solve part of problems when infra will find all book mentioned in schema and doesn't create estore / mstore queue until books have been created

book-description: book1-description
book2:
book-creation-time: 2023-03-27T12:00:00
book-full-name: book1-full-name
book3:
book-creation-time: 2023-03-27T12:00:00
book4:
auto-pages:
book1:
page-duration: PT60S
Expand All @@ -44,6 +59,11 @@ spec:

## Release notes

### 1.8.0

+ Feature:
+ Added auto-book functionality

### 1.7.2

+ Bug fix:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ public static void main(String[] args) {
httpServer.run();
resources.add(httpServer);

AutoBookUtils.createBooks(storage, config.getAutoBooks());

resources.add(
new PageManager(
storage,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2023 Exactpro (Exactpro Systems Limited)
*
* 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 com.exactpro.th2.cradle.adm.http;

import com.fasterxml.jackson.annotation.JsonProperty;

import java.time.Instant;

@SuppressWarnings("FieldMayBeFinal")
public class AutoBookConfiguration {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use @JsonNaming annotation on the class with PropertyNamingStrategies.KEBAB_CASE

@JsonProperty("book-full-name")
private String bookFullName = null;
@JsonProperty("book-description")
private String bookDescription = null;
@JsonProperty("book-creation-time")
private Instant bookCreationTime = null;

public String getBookFullName() {
return bookFullName;
}

public String getBookDescription() {
return bookDescription;
}

public Instant getBookCreationTime() {
return bookCreationTime;
}

Nikita-Smirnov-Exactpro marked this conversation as resolved.
Show resolved Hide resolved
@Override
public String toString() {
return "AutoBookConfiguration{" +
"bookFullName='" + bookFullName + '\'' +
", bookDescription='" + bookDescription + '\'' +
", bookCreationTime=" + bookCreationTime +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright 2023 Exactpro (Exactpro Systems Limited)
*
* 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 com.exactpro.th2.cradle.adm.http;

import com.exactpro.cradle.BookId;
import com.exactpro.cradle.BookInfo;
import com.exactpro.cradle.BookToAdd;
import com.exactpro.cradle.CradleStorage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Instant;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static java.util.Objects.requireNonNull;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import static org.apache.commons.lang3.StringUtils.lowerCase;
import static org.apache.commons.lang3.StringUtils.trim;

public class AutoBookUtils {
Nikita-Smirnov-Exactpro marked this conversation as resolved.
Show resolved Hide resolved
private static final Logger LOGGER = LoggerFactory.getLogger(AutoBookUtils.class);
public static final String AUTO_BOOK_DESCRIPTION = "auto-book";

public static void createBooks(@NotNull CradleStorage storage, @Nullable Map<String, AutoBookConfiguration> autoBooks) {
requireNonNull(storage, "Cradle storage can't be null");

if (autoBooks == null || autoBooks.isEmpty()) {
LOGGER.info("Auto book configuration is empty");
return;
}

Set<String> existedBooks = storage.getBooks().stream()
.map(BookInfo::getId)
.map(BookId::getName)
.collect(Collectors.toSet());

autoBooks.forEach((bookName, config) -> {
try {
bookName = lowerCase(trim(bookName));
if (bookName == null || bookName.isEmpty()) {
LOGGER.warn("book with null or empty name can't be created");
Nikita-Smirnov-Exactpro marked this conversation as resolved.
Show resolved Hide resolved
}

if (existedBooks.contains(bookName)) {
return;
}

BookToAdd bookToAdd;
if (config == null) {
bookToAdd = new BookToAdd(bookName);
bookToAdd.setFullName(bookName);
bookToAdd.setDesc(AUTO_BOOK_DESCRIPTION);
} else {
bookToAdd = new BookToAdd(
bookName,
config.getBookCreationTime() != null ? config.getBookCreationTime() : Instant.now()
);
bookToAdd.setFullName(trim(defaultIfBlank(config.getBookFullName(), bookName)));
bookToAdd.setDesc(trim(defaultIfBlank(config.getBookDescription(), AUTO_BOOK_DESCRIPTION)));
}

storage.addBook(bookToAdd);

LOGGER.info("Created '{}' book, time: {}, full name: {}, description: {}",
bookName,
bookToAdd.getCreated(),
bookToAdd.getFullName(),
bookToAdd.getDesc());
} catch (Exception e) {
throw new RuntimeException("Book with name '" + bookName + "' and config " + config + " can't be created", e);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*******************************************************************************
/*
* Copyright 2023 Exactpro (Exactpro Systems Limited)
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -12,7 +12,7 @@
* 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 com.exactpro.th2.cradle.adm.http;

import com.fasterxml.jackson.annotation.JsonProperty;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package com.exactpro.th2.cradle.adm.http;

import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.Collections;
import java.util.Map;

@SuppressWarnings("FieldMayBeFinal")
Expand All @@ -35,8 +37,11 @@ public class Configuration {
@JsonProperty("page-recheck-interval")
private int pageRecheckInterval = DEFAULT_PAGE_RECHECK_INTERVAL_SEC;

@JsonProperty("auto-books")
private Map<String, AutoBookConfiguration> autoBooks = Collections.emptyMap();

@JsonProperty("auto-pages")
private Map<String, AutoPageConfiguration> autoPages;
private Map<String, AutoPageConfiguration> autoPages = Collections.emptyMap();

public String getIp() {
return ip;
Expand All @@ -46,6 +51,10 @@ public int getPort() {
return port;
}

public Map<String, AutoBookConfiguration> getAutoBooks() {
return autoBooks;
}

public Map<String, AutoPageConfiguration> getAutoPages() {
return autoPages;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
import org.slf4j.LoggerFactory;

public class PageManager implements AutoCloseable, Runnable{
private static final Logger logger = LoggerFactory.getLogger(Application.class);
private static final Logger LOGGER = LoggerFactory.getLogger(PageManager.class);
private static final String AUTO_PAGE_COMMENT = "auto-page";

private final CradleStorage storage;
Expand All @@ -49,7 +49,7 @@ public PageManager(
long pageActionRejectionThreshold
) throws CradleStorageException {
if (autoPages == null || autoPages.isEmpty()) {
logger.info("auto-page configuration is not provided, pages will not be generated automatically");
LOGGER.info("auto-page configuration is not provided, pages will not be generated automatically");
this.storage = null;
this.pageActionRejectionThreshold = 0;
this.executorService = null;
Expand All @@ -65,7 +65,7 @@ public PageManager(
books.put(bookName, new AutoPageInfo(autoPages.get(bookName), storage.refreshBook(bookName)));
}

logger.info("Managing pages for books {} every {} sec", books.keySet().toArray(), pageRecheckInterval);
LOGGER.info("Managing pages for books {} every {} sec", books.keySet().toArray(), pageRecheckInterval);
executorService = Executors.newScheduledThreadPool(1);
executorService.scheduleAtFixedRate(this, 0, pageRecheckInterval, TimeUnit.SECONDS);
}
Expand Down Expand Up @@ -106,7 +106,7 @@ public void close() throws Exception {
executorService.shutdown();
if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
List<Runnable> tasks = executorService.shutdownNow();
logger.warn("Executor can't stop during 5 seconds, " + tasks + " tasks that never commenced execution");
LOGGER.warn("Executor can't stop during 5 seconds, " + tasks + " tasks that never commenced execution");
}
}
}
Expand All @@ -117,7 +117,7 @@ public void run() {
try {
autoPageInfo.setBookInfo(checkBook(autoPageInfo.getBookInfo(), autoPageInfo.getAutoPageConfiguration()));
} catch (Exception e) {
logger.error("Exception processing book {}", bookName, e);
LOGGER.error("Exception processing book {}", bookName, e);
}
});
}
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
release_version = 1.7.2
release_version = 1.8.0
Loading