getRoles() {
return Collections.singleton(user.getRole());
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/auth/AuthenticationDelegate.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.auth;
public interface AuthenticationDelegate {
AuthenticationResult authenticate(String username, String password);
String encodePassword(String rawPassword);
}
================================================
FILE: src/main/java/io/linuxserver/fleet/auth/AuthenticationResult.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.auth;
public class AuthenticationResult {
private final boolean authenticated;
private final AuthenticatedUser user;
public AuthenticationResult(boolean authenticated, AuthenticatedUser user) {
this.authenticated = authenticated;
this.user = user;
}
public static AuthenticationResult notAuthenticated() {
return new AuthenticationResult(false, null);
}
public boolean isAuthenticated() {
return authenticated;
}
public AuthenticatedUser getUser() {
return user;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/auth/DefaultAuthenticationDelegate.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.auth;
import io.linuxserver.fleet.auth.authenticator.UserAuthenticator;
import io.linuxserver.fleet.auth.security.PasswordEncoder;
public class DefaultAuthenticationDelegate implements AuthenticationDelegate {
private final UserAuthenticator authenticator;
public DefaultAuthenticationDelegate(final UserAuthenticator authenticator) {
this.authenticator = authenticator;
}
@Override
public AuthenticationResult authenticate(final String username, final String password) {
return authenticator.authenticate(new UserCredentials(username, password));
}
@Override
public String encodePassword(final String rawPassword) {
return authenticator.getPasswordEncoder().encode(rawPassword);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/auth/UserCredentials.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.auth;
public class UserCredentials {
private final String username;
private final String password;
public UserCredentials(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/auth/authenticator/DefaultUserAuthenticator.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.auth.authenticator;
import io.linuxserver.fleet.auth.AuthenticatedUser;
import io.linuxserver.fleet.auth.AuthenticationResult;
import io.linuxserver.fleet.auth.UserCredentials;
import io.linuxserver.fleet.auth.security.PasswordEncoder;
import io.linuxserver.fleet.v2.service.UserService;
import io.linuxserver.fleet.v2.types.User;
public class DefaultUserAuthenticator implements UserAuthenticator {
private final UserService userService;
private final PasswordEncoder passwordEncoder;
public DefaultUserAuthenticator(final UserService userService,
final PasswordEncoder passwordEncoder) {
this.userService = userService;
this.passwordEncoder = passwordEncoder;
}
@Override
public AuthenticationResult authenticate(final UserCredentials userCredentials) {
final User user = userService.lookUpUser(userCredentials.getUsername());
if (null != user && getPasswordEncoder().matches(userCredentials.getPassword(), user.getPassword())) {
return new AuthenticationResult(true, new AuthenticatedUser(user));
}
return AuthenticationResult.notAuthenticated();
}
@Override
public PasswordEncoder getPasswordEncoder() {
return passwordEncoder;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/auth/authenticator/UserAuthenticator.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.auth.authenticator;
import io.linuxserver.fleet.auth.AuthenticationResult;
import io.linuxserver.fleet.auth.UserCredentials;
import io.linuxserver.fleet.auth.security.PasswordEncoder;
/**
*
* Provides a mechanism for the application to authenticate a login request
* from a user.
*
*/
public interface UserAuthenticator {
/**
*
* Performs an authentication check against the provided credentials and the repository
* of currently stored users.
*
*/
AuthenticationResult authenticate(UserCredentials userCredentials);
PasswordEncoder getPasswordEncoder();
}
================================================
FILE: src/main/java/io/linuxserver/fleet/auth/security/PBKDF2PasswordEncoder.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.auth.security;
import io.linuxserver.fleet.auth.security.util.SaltGenerator;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.Base64;
/**
*
* Uses the PBKDF2 crypto algorithm to encode and verify hashed passwords.
*
*/
public class PBKDF2PasswordEncoder implements PasswordEncoder {
private static final int DEFAULT_HASH_WIDTH = 512;
private static final int DEFAULT_ITERATIONS = 150051;
private static final String PBKDF2 = "PBKDF2WithHmacSHA512";
private final SaltGenerator saltGenerator = new SaltGenerator();
private final byte[] secret;
private final int hashWidth;
private final int iterations;
public PBKDF2PasswordEncoder(String secret) {
this(secret, DEFAULT_HASH_WIDTH, DEFAULT_ITERATIONS);
}
public PBKDF2PasswordEncoder(String secret, int hashWidth, int iterations) {
this.secret = secret.getBytes(StandardCharsets.UTF_8);
this.hashWidth = hashWidth;
this.iterations = iterations;
}
@Override
public String encode(String rawPassword) {
if (null == rawPassword) {
throw new IllegalArgumentException("Password must not be null");
}
return toBase64(encode(rawPassword, saltGenerator.generateSalt()));
}
@Override
public boolean matches(String rawPassword, String encodedPassword) {
byte[] decodedHash = fromBase64(encodedPassword);
byte[] saltInHash = extractSalt(decodedHash);
byte[] hashToVerify = encode(rawPassword, saltInHash);
return passwordsMatch(decodedHash, hashToVerify);
}
/**
*
* Compares the two byte arrays by performing a bitwise equality check against each individual
* element of both arrays.
*
*
* @implNote I looked at a couple of implementations for doing this, and I preferred how Spring had implemented it.
*/
private boolean passwordsMatch(byte[] originalPassword, byte[] providedPassword) {
if (originalPassword.length != providedPassword.length) {
return false;
}
int result = 0;
for (int i = 0; i < originalPassword.length; i++) {
result |= originalPassword[i] ^ providedPassword[i];
}
return result == 0;
}
/**
*
* Performs the cryptographic hash against the raw password and the randomly generated salt value. This
* also concatenates the provided secret into the salt.
*
*/
private byte[] encode(String rawPassword, byte[] salt) {
try {
PBEKeySpec spec = new PBEKeySpec(
rawPassword.toCharArray(),
joinArrays(salt, secret),
iterations,
hashWidth
);
return joinArrays(salt, SecretKeyFactory.getInstance(PBKDF2).generateSecret(spec).getEncoded());
} catch (GeneralSecurityException e) {
throw new IllegalStateException("Unable to create password hash", e);
}
}
/**
*
* Converts a byte array into a base-64 encoded string.
*
*/
private String toBase64(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}
/**
*
* Converts a base64-encoded string into its raw byte value
*
*/
private byte[] fromBase64(String input) {
return Base64.getDecoder().decode(input);
}
/**
*
* Obtains the specific bytes which represent the salt used in a previous hashed password. This is to
* enable the comparision between the existing and new password.
*
*/
private byte[] extractSalt(byte[] decodedHash) {
return extractFromArray(decodedHash, 0, saltGenerator.getKeyLength());
}
/**
*
* Combines two byte arrays together in order.
*
*/
private byte[] joinArrays(byte[] first, byte[] second) {
byte[] result = Arrays.copyOf(first, first.length + second.length);
System.arraycopy(second, 0, result, first.length, second.length);
return result;
}
/**
*
* Extracts a sub-array from the provided array.
*
*/
private byte[] extractFromArray(byte[] array, int begin, int end) {
int length = end - begin;
byte[] subarray = new byte[length];
System.arraycopy(array, begin, subarray, 0, length);
return subarray;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/auth/security/PasswordEncoder.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.auth.security;
/**
*
* Provides a mechanism for a password to be encoded using a strong cryptographic algorithm.
* The general idea of this interface has been taken from Spring's own implementation of this.
* PasswordEncoder
*
*/
public interface PasswordEncoder {
/**
*
* Encodes the raw password into a one-way encrypted hash. The result of which should be stored.
*
*
* @param rawPassword
* The raw unencrypted password.
*
* @return
* The hashed result.
*/
String encode(String rawPassword);
/**
*
* Determines if the provided raw password, when encoded, matches the stored encoded password.
*
*
* @param rawPassword
* The raw password to check
* @param encodedPassword
* The originally encoded and stored password
* @return
* true if the passwords match, false if not.
*/
boolean matches(String rawPassword, String encodedPassword);
}
================================================
FILE: src/main/java/io/linuxserver/fleet/auth/security/util/SaltGenerator.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.auth.security.util;
import java.security.SecureRandom;
public class SaltGenerator {
private static final int KEY_LENGTH = 16;
public byte[] generateSalt() {
SecureRandom sr = new SecureRandom();
byte[] salt = new byte[KEY_LENGTH];
sr.nextBytes(salt);
return salt;
}
public int getKeyLength() {
return KEY_LENGTH;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/core/AbstractAppController.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.core;
import io.linuxserver.fleet.core.config.AppProperties;
import io.linuxserver.fleet.core.db.DatabaseProvider;
import io.linuxserver.fleet.core.db.DefaultDatabaseProvider;
import io.linuxserver.fleet.db.DefaultDatabaseConnection;
import io.linuxserver.fleet.v2.cache.BasicItemCache;
import io.linuxserver.fleet.v2.key.AlertKey;
import io.linuxserver.fleet.v2.types.AppAlert;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public abstract class AbstractAppController {
private final AppProperties appProperties;
private final DatabaseProvider databaseProvider;
private final BasicItemCache alertCache;
public AbstractAppController() {
this.appProperties = new PropertiesLoader().getProperties();
this.databaseProvider = new DefaultDatabaseProvider(new DefaultDatabaseConnection(appProperties.getDatabaseProperties()));
this.alertCache = new BasicItemCache<>();
}
public final DatabaseProvider getDatabaseProvider() {
return databaseProvider;
}
public final AppProperties getAppProperties() {
return appProperties;
}
public final List getAlerts() {
return new ArrayList<>(alertCache.getAllItems());
}
public final List getSystemAlerts() {
return getAlerts().stream().filter(AppAlert::isSystemAlert).collect(Collectors.toList());
}
public final void addAlert(final AppAlert appAlert) {
alertCache.addItem(appAlert);
}
public final void clearAlert(final AlertKey alertKey) {
alertCache.removeItem(alertKey);
}
protected void run() {
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/core/BaseRuntimeLoader.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.core;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* Prints out all runtime arguments passed in as JVM arguments (-D).
*
*
* @author Josh Stark
*/
abstract class BaseRuntimeLoader {
private static final Logger LOGGER = LoggerFactory.getLogger(BaseRuntimeLoader.class);
BaseRuntimeLoader() {
LOGGER.info("Initalising...");
LOGGER.info("Config base : " + FleetRuntime.CONFIG_BASE);
LOGGER.info("Show Passwords : " + FleetRuntime.SHOW_PASSWORDS);
LOGGER.info("Nuke database : " + FleetRuntime.NUKE_DATABASE);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/core/FleetAppController.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.core;
import io.linuxserver.fleet.auth.AuthenticationResult;
import io.linuxserver.fleet.core.config.WebConfiguration;
import io.linuxserver.fleet.v2.client.docker.DockerApiClient;
import io.linuxserver.fleet.v2.client.docker.dockerhub.DockerHubApiClient;
import io.linuxserver.fleet.v2.client.docker.dockerhub.DockerHubAuthenticator;
import io.linuxserver.fleet.v2.client.docker.dockerhub.IDockerHubAuthenticator;
import io.linuxserver.fleet.v2.client.docker.dockerhub.NoOpDockerHubAuthenticator;
import io.linuxserver.fleet.v2.client.docker.queue.DockerApiDelegate;
import io.linuxserver.fleet.v2.client.rest.RestClient;
import io.linuxserver.fleet.v2.db.DefaultImageDAO;
import io.linuxserver.fleet.v2.db.DefaultScheduleDAO;
import io.linuxserver.fleet.v2.db.DefaultUserDAO;
import io.linuxserver.fleet.v2.file.FileManager;
import io.linuxserver.fleet.v2.key.ImageKey;
import io.linuxserver.fleet.v2.service.ImageService;
import io.linuxserver.fleet.v2.service.ScheduleService;
import io.linuxserver.fleet.v2.service.SynchronisationService;
import io.linuxserver.fleet.v2.service.UserService;
import io.linuxserver.fleet.v2.types.Image;
import io.linuxserver.fleet.v2.types.Repository;
import io.linuxserver.fleet.v2.types.internal.RepositoryOutlineRequest;
import io.linuxserver.fleet.v2.web.WebRouteController;
/**
*
* Primary entry point for the application. All contexts and resources are loaded
* through this class.
*
*/
public class FleetAppController extends AbstractAppController implements ServiceProvider {
private final DockerApiDelegate dockerApiDelegate;
private final ImageService imageService;
private final ScheduleService scheduleService;
private final SynchronisationService syncService;
private final UserService userService;
private final FileManager fileManager;
public FleetAppController() {
fileManager = new FileManager(this);
imageService = new ImageService(this, new DefaultImageDAO(getDatabaseProvider()));
scheduleService = new ScheduleService(this, new DefaultScheduleDAO(getDatabaseProvider()));
dockerApiDelegate = new DockerApiDelegate(this, configureDockerApiClient());
syncService = new SynchronisationService(this);
userService = new UserService(this, new DefaultUserDAO(getDatabaseProvider()));
}
private static FleetAppController instance;
public static FleetAppController instance() {
if (null == instance) {
synchronized (FleetAppController.class) {
if (null == instance) {
instance = new FleetAppController();
}
}
}
return instance;
}
@Override
protected final void run() {
super.run();
configureWeb();
scheduleService.initialiseSchedules();
}
public final WebConfiguration getWebConfiguration() {
return new WebConfiguration(getAppProperties());
}
private void configureWeb() {
new WebRouteController(this);
}
public final void handleException(final Exception e) {
}
public final boolean synchroniseImage(final ImageKey imageKey) {
return syncService.synchroniseImage(imageKey);
}
public final void synchroniseRepository(final Repository repository) {
syncService.synchroniseCachedRepository(repository);
}
public final DockerApiDelegate getConfiguredDockerDelegate() {
return dockerApiDelegate;
}
public final ImageService getImageService() {
return imageService;
}
public final Image storeUpdatedImage(final Image updatedImage) {
return imageService.storeImage(updatedImage);
}
@Override
public ScheduleService getScheduleService() {
return scheduleService;
}
public final Repository verifyRepositoryAndCreateOutline(final RepositoryOutlineRequest request) {
if (getConfiguredDockerDelegate().isRepositoryValid(request.getRepositoryName())) {
final Repository repositoryOutline = getImageService()
.createRepositoryOutline(new RepositoryOutlineRequest(request.getRepositoryName()));
getSynchronisationService().synchroniseUpstreamRepository(repositoryOutline);
return repositoryOutline;
}
throw new IllegalArgumentException("Repository " + request.getRepositoryName() + " does not exist upstream");
}
@Override
public final SynchronisationService getSynchronisationService() {
return syncService;
}
@Override
public final UserService getUserService() {
return userService;
}
@Override
public FileManager getFileManager() {
return fileManager;
}
public final AuthenticationResult authenticateCredentials(final String username, final String password) {
return userService.authenticateCredentials(username, password);
}
public final void trackBranch(final ImageKey imageKey, final String branchName) {
getImageService().trackBranchOnImage(imageKey, branchName);
synchroniseImage(imageKey);
}
private DockerApiClient configureDockerApiClient() {
final RestClient dockerHubApiRestClient = new RestClient();
final IDockerHubAuthenticator dockerHubAuthenticator;
if (getAppProperties().isDockerHubAuthEnabled()) {
dockerHubAuthenticator = new DockerHubAuthenticator(getAppProperties().getDockerHubCredentials(), dockerHubApiRestClient);
} else {
dockerHubAuthenticator = new NoOpDockerHubAuthenticator();
}
return new DockerHubApiClient(dockerHubApiRestClient, dockerHubAuthenticator);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/core/FleetRuntime.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.core;
public interface FleetRuntime {
/**
* If set will switch specific properties to allow more streamlined development
*/
boolean DEV_MODE = System.getProperty("enable.dev") != null;
/**
* Base directory for the config file.
*/
String CONFIG_BASE = System.getProperty("fleet.config.base");
/**
* Whether or not logs should show passwords
*/
boolean SHOW_PASSWORDS = System.getProperty("fleet.show.passwords") != null;
/**
* Tells Fleet to completely wipe the database and recreate it.
*/
boolean NUKE_DATABASE = System.getProperty("fleet.nuke.database") != null;
}
================================================
FILE: src/main/java/io/linuxserver/fleet/core/Main.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.core;
public class Main {
public static void main(String[] args) {
FleetAppController.instance().run();
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/core/PropertiesLoader.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.core;
import io.linuxserver.fleet.core.config.AppProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Objects;
import java.util.Properties;
/**
*
* Loads in the application properties from disk. Properties must be provided as JVM arguments
* as these will be used to create a connection to the underlying database, and any other
* required connections.
*
*
* @author Josh Stark
*/
class PropertiesLoader extends BaseRuntimeLoader {
private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesLoader.class);
private final AppProperties properties;
PropertiesLoader() {
super();
try {
createConfigFileIfNotProvided();
Properties properties = new Properties();
properties.load(new FileInputStream(FleetRuntime.CONFIG_BASE + "/fleet.properties"));
properties.load(Objects.requireNonNull(getClass().getClassLoader().getResourceAsStream("version.properties")));
properties.setProperty("fleet.static.dirname", "fleet_static");
this.properties = new AppProperties(properties);
printProperties();
if (!createStaticFileDirectory() || !createLogsDirectory()) {
throw new RuntimeException("Unable to create config sub directories for Fleet. Check permissions");
}
} catch (IOException e) {
LOGGER.error("Unable to load config! Check JVM args and config directory", e);
throw new RuntimeException(e);
}
}
private void createConfigFileIfNotProvided() {
try {
File configFile = new File(FleetRuntime.CONFIG_BASE + "/fleet.properties");
if (!configFile.exists()) {
if (!configFile.createNewFile()) {
throw new RuntimeException("Unable to create base config for fleet.");
}
}
} catch (IOException e) {
throw new RuntimeException("Unable to create base config for fleet.", e);
}
}
private boolean createStaticFileDirectory() {
File staticFilesDir = new File(properties.getStaticFilesPath().toString());
if (staticFilesDir.exists()) {
return true;
}
return staticFilesDir.mkdir();
}
private boolean createLogsDirectory() {
File logsDirectory = new File(FleetRuntime.CONFIG_BASE + "/logs");
if (logsDirectory.exists()) {
return true;
}
return logsDirectory.mkdir();
}
/**
*
* The loaded properties, with accessible fields.
*
*
* @return
* All application properties.
*/
AppProperties getProperties() {
return properties;
}
/**
*
* Prints out the loaded properties to the log. Useful when the application loads up for the first time.
*
*/
private void printProperties() {
LOGGER.info("fleet.app.port : " + properties.getAppPort());
LOGGER.info("fleet.database.url : " + properties.getDatabaseProperties().getDatabaseUrl());
LOGGER.info("fleet.database.username : " + properties.getDatabaseProperties().getDatabaseUsername());
LOGGER.info("fleet.database.password : " + (showPasswords() ? properties.getDatabaseProperties().getDatabasePassword() : "***"));
LOGGER.info("app.version : " + getProperties().getVersionProperties());
}
/**
*
* Ideally in a production environment you don't want to log passwords or keys.
*
*
* @return
* true if passwords can be logged to the terminal.
*/
private boolean showPasswords() {
return FleetRuntime.SHOW_PASSWORDS;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/core/ServiceProvider.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.core;
import io.linuxserver.fleet.v2.file.FileManager;
import io.linuxserver.fleet.v2.service.ImageService;
import io.linuxserver.fleet.v2.service.ScheduleService;
import io.linuxserver.fleet.v2.service.SynchronisationService;
import io.linuxserver.fleet.v2.service.UserService;
public interface ServiceProvider {
SynchronisationService getSynchronisationService();
ImageService getImageService();
ScheduleService getScheduleService();
UserService getUserService();
FileManager getFileManager();
}
================================================
FILE: src/main/java/io/linuxserver/fleet/core/config/AppProperties.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.core.config;
import io.linuxserver.fleet.core.FleetRuntime;
import io.linuxserver.fleet.v2.client.docker.dockerhub.DockerHubCredentials;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Properties;
public class AppProperties {
private Properties properties;
public AppProperties(final Properties properties) {
this.properties = properties;
}
public DatabaseConnectionProperties getDatabaseProperties() {
return new DatabaseConnectionProperties(getDatabaseDriverClassName(),
getDatabaseUrl(),
getDatabaseUsername(),
getDatabasePassword());
}
public final VersionProperties getVersionProperties() {
return new VersionProperties(getStringProperty("app.version"),
getStringProperty("app.build.user"),
getStringProperty("app.build.date"),
getStringProperty("app.build.os"));
}
private String getDatabaseDriverClassName() {
return getStringProperty("fleet.database.driver");
}
private String getDatabaseUrl() {
return getStringProperty("fleet.database.url");
}
private String getDatabaseUsername() {
return getStringProperty("fleet.database.username");
}
private String getDatabasePassword() {
return getStringProperty("fleet.database.password");
}
public final Path getStaticFilesPath() {
return Paths.get(FleetRuntime.CONFIG_BASE, getStringProperty("fleet.static.dirname")).toAbsolutePath();
}
public String getAppSecret() {
String secret = getStringProperty("fleet.admin.secret");
return null == secret ? "" : secret;
}
public int getAppPort() {
return Integer.parseInt(getStringProperty("fleet.app.port"));
}
public boolean isDockerHubAuthEnabled() {
return "true".equalsIgnoreCase(getStringProperty("fleet.dockerhub.auth.enabled"));
}
public DockerHubCredentials getDockerHubCredentials() {
final String username = getStringProperty("fleet.dockerhub.username");
final String password = getStringProperty("fleet.dockerhub.password");
return new DockerHubCredentials(username, password);
}
/**
*
* Obtains the property value from three separate sources: first from the config file. If not present, it will look
* at the JVM runtime. If that is not present, it will finally check the system environment.
*
*/
private String getStringProperty(String propertyKey) {
String property = properties.getProperty(propertyKey);
if (null == property) {
property = System.getProperty(propertyKey);
if (null == property) {
property = System.getenv(propertyKey.replace(".", "_"));
}
}
return property;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/core/config/DatabaseConnectionProperties.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.core.config;
public class DatabaseConnectionProperties {
private final String driverClass;
private final String url;
private final String username;
private final String password;
public DatabaseConnectionProperties(final String driverClass,
final String url,
final String username,
final String password) {
this.driverClass = driverClass;
this.url = url;
this.username = username;
this.password = password;
}
public final String getDatabaseDriverClass() {
return driverClass;
}
public final String getDatabaseUrl() {
return url;
}
public final String getDatabaseUsername() {
return username;
}
public final String getDatabasePassword() {
return password;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/core/config/Version.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.core.config;
public class Version {
private int major;
private int minor;
private int patch;
public Version(int major, int minor, int patch) {
this.major = major;
this.minor = minor;
this.patch = patch;
}
public Version(String version) {
String[] bits = version.split("\\.");
this.major = Integer.parseInt(bits[0]);
this.minor = Integer.parseInt(bits[1]);
this.patch = Integer.parseInt(bits[2]);
}
public int getMajor() {
return major;
}
public int getMinor() {
return minor;
}
public int getPatch() {
return patch;
}
public boolean isNewerThan(Version version) {
if (this.major > version.major) {
return true;
} if (this.minor > version.minor) {
return true;
} else if (this.minor < version.minor) {
return false;
}
return this.patch > version.patch;
}
@Override
public String toString() {
return major + "." + minor + "." + patch;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/core/config/VersionProperties.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.core.config;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class VersionProperties {
private final Version version;
private final String buildUser;
private final LocalDateTime buildDate;
private final String buildPlatform;
public VersionProperties(final String version,
final String buildUser,
final String buildDate,
final String buildPlatform) {
this.version = new Version(version);
this.buildUser = buildUser;
this.buildDate = LocalDateTime.parse(buildDate, DateTimeFormatter.ISO_DATE_TIME);
this.buildPlatform = buildPlatform;
}
public final Version getVersion() {
return version;
}
public final String getBuildUser() {
return buildUser;
}
public final LocalDateTime getBuildDate() {
return buildDate;
}
public final String getBuildPlatform() {
return buildPlatform;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/core/config/WebConfiguration.java
================================================
/*
* Copyright (c) 2019 Wallett
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.core.config;
public class WebConfiguration {
private final AppProperties appProperties;
public WebConfiguration(final AppProperties properties) {
appProperties = properties;
}
public final int getPort() {
return appProperties.getAppPort();
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/core/db/DatabaseConnection.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.core.db;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public interface DatabaseConnection {
DataSource getDataSource();
Connection getConnection() throws SQLException;
}
================================================
FILE: src/main/java/io/linuxserver/fleet/core/db/DatabaseProvider.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.core.db;
import io.linuxserver.fleet.db.migration.DatabaseVersion;
public interface DatabaseProvider {
DatabaseConnection getDatabaseConnection();
DatabaseVersion getVersionHandler();
}
================================================
FILE: src/main/java/io/linuxserver/fleet/core/db/DefaultDatabaseProvider.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.core.db;
import io.linuxserver.fleet.db.migration.DatabaseVersion;
public class DefaultDatabaseProvider implements DatabaseProvider {
private final DatabaseConnection databaseConnection;
private final DatabaseVersion databaseVersion;
public DefaultDatabaseProvider(final DatabaseConnection databaseConnection) {
this.databaseConnection = databaseConnection;
this.databaseVersion = new DatabaseVersion(databaseConnection);
}
@Override
public DatabaseConnection getDatabaseConnection() {
return databaseConnection;
}
@Override
public DatabaseVersion getVersionHandler() {
return databaseVersion;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/db/DefaultDatabaseConnection.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.db;
import io.linuxserver.fleet.core.config.DatabaseConnectionProperties;
public class DefaultDatabaseConnection extends PoolingDatabaseConnection {
public DefaultDatabaseConnection(final DatabaseConnectionProperties properties) {
super(properties);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/db/PoolingDatabaseConnection.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public LicensedatabaseConnection.getDataSource(
* along with this program. If not, see .
*/
package io.linuxserver.fleet.db;
import com.zaxxer.hikari.HikariDataSource;
import io.linuxserver.fleet.core.config.DatabaseConnectionProperties;
import io.linuxserver.fleet.core.db.DatabaseConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public abstract class PoolingDatabaseConnection implements DatabaseConnection {
private static final Logger LOGGER = LoggerFactory.getLogger(PoolingDatabaseConnection.class);
private final HikariDataSource dataSource;
PoolingDatabaseConnection(final DatabaseConnectionProperties properties) {
dataSource = new HikariDataSource();
dataSource.setDriverClassName(properties.getDatabaseDriverClass());
dataSource.setJdbcUrl(properties.getDatabaseUrl());
dataSource.setUsername(properties.getDatabaseUsername());
dataSource.setPassword(properties.getDatabasePassword());
LOGGER.info("DataSource established: " + dataSource.getJdbcUrl());
}
@Override
public DataSource getDataSource() {
return dataSource;
}
@Override
public final Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/db/dao/Utils.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.db.dao;
import java.sql.CallableStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.LocalDateTime;
class Utils {
static void setNullableInt(CallableStatement call, int position, Integer value) throws SQLException {
if (null == value)
call.setNull(position, Types.INTEGER);
else
call.setInt(position, value);
}
static void setNullableLong(CallableStatement call, int position, Long value) throws SQLException {
if (null == value)
call.setNull(position, Types.BIGINT);
else
call.setLong(position, value);
}
static void setNullableString(CallableStatement call, int position, String value) throws SQLException {
if (null == value)
call.setNull(position, Types.VARCHAR);
else
call.setString(position, value);
}
static void setNullableTimestamp(CallableStatement call, int position, LocalDateTime localDateTime) throws SQLException {
if (null == localDateTime) {
call.setNull(position, Types.TIMESTAMP);
} else {
call.setTimestamp(position, Timestamp.valueOf(localDateTime));
}
}
static void safeClose(CallableStatement call) {
try {
if (null != call)
call.close();
} catch (SQLException e) {
throw new RuntimeException("Unable to close call", e);
}
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/db/migration/DatabaseVersion.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.db.migration;
import io.linuxserver.fleet.core.FleetRuntime;
import io.linuxserver.fleet.core.db.DatabaseConnection;
import org.flywaydb.core.Flyway;
import org.flywaydb.core.api.FlywayException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* Manages versioning of the database to ensure it is kept up-to-date
*
*/
public class DatabaseVersion {
private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseVersion.class);
private final Flyway flyway;
public DatabaseVersion(final DatabaseConnection databaseConnection) {
flyway = Flyway.configure().dataSource(databaseConnection.getDataSource()).load();
migrate();
}
/**
*
* Runs the migration process which runs any necessary scripts to get the database configured.
*
*/
public void migrate() {
try {
if (FleetRuntime.NUKE_DATABASE) {
flyway.clean();
}
flyway.migrate();
} catch (FlywayException e) {
LOGGER.error(e.getMessage());
throw new RuntimeException("Unable to start application because the database has gone out of sync.", e);
}
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/db/query/InsertUpdateResult.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.db.query;
public class InsertUpdateResult {
private final T result;
private final int status;
private final String statusMessage;
public InsertUpdateResult(T result) {
this(result, InsertUpdateStatus.OK, "OK");
}
public InsertUpdateResult(T result, int status, String statusMessage) {
this.result = result;
this.status = status;
this.statusMessage = statusMessage;
}
public InsertUpdateResult(int status, String statusMessage) {
this(null, status, statusMessage);
}
public final T getResult() {
return result;
}
public final int getStatus() {
return status;
}
public final String getStatusMessage() {
return statusMessage;
}
public final boolean isError() {
return status != InsertUpdateStatus.OK;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/db/query/InsertUpdateStatus.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.db.query;
public interface InsertUpdateStatus {
int OK = 0;
int FAILED = 1;
}
================================================
FILE: src/main/java/io/linuxserver/fleet/db/query/LimitOffset.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.db.query;
public class LimitOffset {
private final int limit;
private final int offset;
public LimitOffset(int limit, int offset) {
this.limit = limit;
this.offset = offset;
}
public int getLimit() {
return limit;
}
public int getOffset() {
return offset;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/db/query/LimitedResult.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.db.query;
import java.util.List;
public class LimitedResult {
private final List results;
private final LimitOffset next;
private final int totalCount;
public LimitedResult(List results, int totalCount) {
this(results, totalCount,null);
}
public LimitedResult(List results, int totalCount, LimitOffset next) {
this.results = results;
this.totalCount = totalCount;
this.next = next;
}
public List getResults() {
return results;
}
public int getTotalCount() {
return totalCount;
}
public LimitOffset getNext() {
return next;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/dockerhub/DockerHubException.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.dockerhub;
public class DockerHubException extends RuntimeException {
public DockerHubException(String message) {
super(message);
}
public DockerHubException(String message, Throwable cause) {
super(message, cause);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/dockerhub/model/DockerHubV2Image.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.dockerhub.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class DockerHubV2Image {
@JsonProperty("user")
private String user;
@JsonProperty("name")
private String name;
@JsonProperty("namespace")
private String namespace;
@JsonProperty("description")
private String description;
@JsonProperty("star_count")
private int starCount;
@JsonProperty("pull_count")
private long pullCount;
@JsonProperty("last_updated")
private String lastUpdated;
public final String getUser() {
return user;
}
public final String getName() {
return name;
}
public final String getNamespace() {
return namespace;
}
public String getDescription() {
return description;
}
public final int getStarCount() {
return starCount;
}
public final long getPullCount() {
return pullCount;
}
public final String getLastUpdated() {
return lastUpdated;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/dockerhub/model/DockerHubV2ImageListResult.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.dockerhub.model;
public class DockerHubV2ImageListResult extends DockerHubV2ScanResult {
}
================================================
FILE: src/main/java/io/linuxserver/fleet/dockerhub/model/DockerHubV2NamespaceLookupResult.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.dockerhub.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.List;
public class DockerHubV2NamespaceLookupResult {
@JsonProperty("namespaces")
private List namespaces = new ArrayList<>();
public List getNamespaces() {
return namespaces;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/dockerhub/model/DockerHubV2ScanResult.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.dockerhub.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public class DockerHubV2ScanResult {
@JsonProperty("count")
private int count;
@JsonProperty("next")
private String next;
@JsonProperty("previous")
private String previous;
@JsonProperty("results")
private List results;
public final int getCount() {
return count;
}
public final String getNext() {
return next;
}
public final String getPrevious() {
return previous;
}
public final List getResults() {
return results;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/dockerhub/model/DockerHubV2Tag.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.dockerhub.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
import java.util.Objects;
public class DockerHubV2Tag {
@JsonProperty("name")
private String name;
@JsonProperty("full_size")
private long fullSize;
@JsonProperty("last_updated")
private String lastUpdated;
@JsonProperty("images")
private List images;
public String getName() {
return name;
}
public long getFullSize() {
return fullSize;
}
public String getLastUpdated() {
return lastUpdated;
}
public List getImages() {
return images;
}
@Override
public String toString() {
return name;
}
@Override
public int hashCode() {
return Objects.hash(name, fullSize);
}
@Override
public boolean equals(Object other) {
if (!(other instanceof DockerHubV2Tag)) {
return false;
}
if (other == this) {
return true;
}
DockerHubV2Tag otherTag = (DockerHubV2Tag) other;
return name.equals(otherTag.name) && fullSize == otherTag.fullSize;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/dockerhub/model/DockerHubV2TagDigest.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.dockerhub.model;
import com.fasterxml.jackson.annotation.JsonProperty;
public class DockerHubV2TagDigest {
@JsonProperty("size")
private long size;
@JsonProperty("digest")
private String digest;
@JsonProperty("architecture")
private String architecture;
@JsonProperty("variant")
private String variant;
public long getSize() {
return size;
}
public String getDigest() {
return digest;
}
public String getArchitecture() {
return architecture;
}
public String getVariant() {
return variant;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/dockerhub/model/DockerHubV2TagListResult.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.dockerhub.model;
public class DockerHubV2TagListResult extends DockerHubV2ScanResult {
}
================================================
FILE: src/main/java/io/linuxserver/fleet/dockerhub/util/DockerTagFinder.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.dockerhub.util;
import io.linuxserver.fleet.v2.types.docker.DockerTag;
import io.linuxserver.fleet.v2.types.docker.DockerTagManifestDigest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class DockerTagFinder {
public static DockerTag findVersionedTagMatchingBranch(List tags, String namedBranch) {
Optional tagBranchName = tags.stream().filter(tag -> namedBranch.equals(tag.getName())).findFirst();
if (tagBranchName.isPresent()) {
DockerTag namedTagForBranch = tagBranchName.get();
Optional versionedLatestTag = tags.stream()
.filter(tag -> !tag.equals(namedTagForBranch) && allManifestsMatch(namedTagForBranch, tag)).findFirst();
return versionedLatestTag.orElse(namedTagForBranch);
}
return tags.isEmpty() ? null : tags.get(0);
}
private static boolean allManifestsMatch(final DockerTag namedTag, final DockerTag toCheck) {
final List namedDigests = namedTag.getDigests();
final List digestsToCheck = toCheck.getDigests();
boolean allMatch = true;
if (namedDigests.size() == digestsToCheck.size()) {
final Map namedDigestsAsMap = toMapKeyedByArch(namedDigests);
for (DockerTagManifestDigest digestToCheck : digestsToCheck) {
final String archPlusVariant = digestToCheck.getArchitecture() + digestToCheck.getArchVariant();
final String foundDigest = namedDigestsAsMap.get(archPlusVariant);
allMatch = allMatch && (null != foundDigest) && foundDigest.equals(digestToCheck.getDigest());
}
} else {
allMatch = false;
}
return allMatch;
}
private static Map toMapKeyedByArch(final List initialList) {
final Map map = new HashMap<>();
for (DockerTagManifestDigest digest : initialList) {
map.put(digest.getArchitecture() + digest.getArchVariant(), digest.getDigest());
}
return map;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/exception/SaveException.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.exception;
public class SaveException extends Exception {
public SaveException(String message) {
super(message);
}
public SaveException(String message, Throwable cause) {
super(message, cause);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/LoggerOwner.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2;
import org.slf4j.Logger;
public interface LoggerOwner {
Logger getLogger();
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/Utils.java
================================================
/*
* Copyright (c) 2020 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2;
public final class Utils {
public static T ensureNotNull(final T obj) {
if (null == obj) {
throw new IllegalArgumentException("Parameter null");
}
return obj;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/cache/AbstractItemCache.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.cache;
import io.linuxserver.fleet.v2.key.HasKey;
import io.linuxserver.fleet.v2.key.Key;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
public abstract class AbstractItemCache> implements ItemCache {
private final Logger LOGGER = LoggerFactory.getLogger(getClass());
private final List> listeners;
private final Map items;
public AbstractItemCache() {
listeners = new ArrayList<>();
items = new HashMap<>();
}
public final void clear() {
LOGGER.info("Emptying cache");
items.clear();
}
public final void registerCacheListener(final ItemCacheListener- listener) {
LOGGER.info("Registering new cache listener {}", listener);
listeners.add(listener);
}
@Override
public final void addItem(final ITEM item) {
final ITEM original = items.get(item.getKey());
final ITEM cached = items.put(item.getKey(), item);
LOGGER.info("Item {} cached", item);
if (null == original) {
listeners.forEach(l -> l.onItemAdded(cached));
} else {
listeners.forEach(l -> l.onItemUpdated(original, cached));
}
}
@Override
public boolean isEmpty() {
return items.isEmpty();
}
@Override
public final ITEM findItem(final KEY key) {
return items.get(key);
}
@Override
public final void removeItem(final KEY key) {
final ITEM removed = items.remove(key);
LOGGER.info("Item {} removed from cache", removed);
listeners.forEach(l -> l.onItemRemoved(removed));
}
@Override
public final boolean isItemCached(final KEY key) {
return items.containsKey(key);
}
@Override
public Collection
- getAllItems() {
return items.values();
}
@Override
public final void addAllItems(Collection
- allItems) {
allItems.forEach(this::addItem);
}
@Override
public int size() {
return items.size();
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/cache/BasicItemCache.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.cache;
import io.linuxserver.fleet.v2.key.HasKey;
import io.linuxserver.fleet.v2.key.Key;
public final class BasicItemCache> extends AbstractItemCache {
// Default implementation
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/cache/ImageCache.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.cache;
import io.linuxserver.fleet.v2.key.ImageKey;
import io.linuxserver.fleet.v2.types.Image;
public class ImageCache extends AbstractItemCache {
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/cache/ItemCache.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.cache;
import io.linuxserver.fleet.v2.key.HasKey;
import io.linuxserver.fleet.v2.key.Key;
import java.util.Collection;
public interface ItemCache> {
boolean isEmpty();
void addItem(ITEM item);
ITEM findItem(KEY key);
void removeItem(KEY key);
boolean isItemCached(KEY key);
Collection
- getAllItems();
void addAllItems(Collection
- items);
int size();
interface ItemCacheListener
- {
void onItemAdded(final ITEM item);
void onItemUpdated(final ITEM oldItem, final ITEM newItem);
void onItemRemoved(final ITEM item);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/cache/RepositoryCache.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.cache;
import io.linuxserver.fleet.v2.key.ImageKey;
import io.linuxserver.fleet.v2.key.ImageLookupKey;
import io.linuxserver.fleet.v2.key.RepositoryKey;
import io.linuxserver.fleet.v2.types.Image;
import io.linuxserver.fleet.v2.types.Repository;
public class RepositoryCache extends AbstractItemCache {
public final Image lookupImage(final ImageLookupKey lookupKey) {
for (Repository repository : getAllItems()) {
for (Image image : repository.getImages()) {
if (lookupKey.isLookupKeyFor(image)) {
return image;
}
}
}
return null;
}
public final Image findImage(final ImageKey imageKey) {
if (isItemCached(imageKey.getRepositoryKey())) {
final Repository repository = findItem(imageKey.getRepositoryKey());
for (Image image : repository.getImages()) {
if (imageKey.equals(image.getKey())) {
return image;
}
}
}
return null;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/cache/ScheduleCache.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.cache;
import io.linuxserver.fleet.v2.key.ScheduleKey;
import io.linuxserver.fleet.v2.thread.schedule.AppSchedule;
public class ScheduleCache extends AbstractItemCache {
public final boolean isScheduleRunning(final ScheduleKey scheduleKey) {
return isItemCached(scheduleKey);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/AbstractDockerApiClient.java
================================================
/*
* Copyright (c) 2020 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker;
import io.linuxserver.fleet.v2.client.docker.converter.DockerResponseConverter;
import io.linuxserver.fleet.v2.types.docker.DockerImage;
import io.linuxserver.fleet.v2.types.docker.DockerTag;
import java.util.List;
import java.util.stream.Collectors;
public abstract class AbstractDockerApiClient, TC extends DockerResponseConverter> implements DockerApiClient {
private final IC imageConverter;
private final TC tagConverter;
public AbstractDockerApiClient(final IC imageConverter, final TC tagConverter) {
this.imageConverter = imageConverter;
this.tagConverter = tagConverter;
}
@Override
public final DockerImage fetchImage(String imageName) {
final D dockerModel = fetchImageFromApi(imageName);
if (null == dockerModel) {
return null;
}
return imageConverter.convert(dockerModel);
}
@Override
public final List fetchAllImages(String repositoryName) {
return fetchAllImagesFromApi(repositoryName).stream().map(imageConverter::convert).collect(Collectors.toList());
}
@Override
public final List fetchImageTags(String imageName) {
return fetchTagsFromApi(imageName).stream().map(tagConverter::convert).collect(Collectors.toList());
}
protected abstract D fetchImageFromApi(final String imageName);
protected abstract List fetchAllImagesFromApi(final String repositoryName);
protected abstract List fetchTagsFromApi(final String imageName);
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/DockerApiClient.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker;
import io.linuxserver.fleet.v2.types.docker.DockerImage;
import io.linuxserver.fleet.v2.types.docker.DockerTag;
import java.util.List;
public interface DockerApiClient {
boolean isRepositoryValid(final String repositoryName);
DockerImage fetchImage(final String imageName);
List fetchAllImages(final String repositoryName);
List fetchImageTags(final String imageName);
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/DockerImageNotFoundException.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker;
public class DockerImageNotFoundException extends RuntimeException {
public DockerImageNotFoundException(final String reason) {
super(reason);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/converter/AbstractDockerResponseConverter.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker.converter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.LocalDateTime;
import java.time.format.DateTimeParseException;
public abstract class AbstractDockerResponseConverter implements DockerResponseConverter {
private final Logger LOGGER = LoggerFactory.getLogger(getClass());
@Override
public final I convert(final D dockerModel) {
try {
if (null == dockerModel) {
LOGGER.warn("Attempted to convert null image");
} else {
return doPlainConvert(dockerModel);
}
} catch (Exception e) {
LOGGER.error("Unable to convert docker model to internal.", e);
}
return null;
}
protected abstract I doPlainConvert(final D dockerApiImage);
protected final LocalDateTime parseDockerHubDate(String date) {
if (null == date) {
return null;
}
try {
final String dateToParse = (date.endsWith("Z") ? date.substring(0, date.length() - 1) : date);
return LocalDateTime.parse(dateToParse);
} catch (DateTimeParseException e) {
LOGGER.error("parseDockerHubDate(" + date + ") unable to leniently parse date.", e);
return null;
}
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/converter/DockerResponseConverter.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker.converter;
public interface DockerResponseConverter {
INTERNAL_MODEL convert(final DOCKER_MODEL dockerModel);
Class getConverterClass();
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/dockerhub/DockerHubApiClient.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker.dockerhub;
import io.linuxserver.fleet.dockerhub.DockerHubException;
import io.linuxserver.fleet.dockerhub.model.DockerHubV2Image;
import io.linuxserver.fleet.dockerhub.model.DockerHubV2ImageListResult;
import io.linuxserver.fleet.dockerhub.model.DockerHubV2Tag;
import io.linuxserver.fleet.dockerhub.model.DockerHubV2TagListResult;
import io.linuxserver.fleet.v2.Utils;
import io.linuxserver.fleet.v2.client.docker.AbstractDockerApiClient;
import io.linuxserver.fleet.v2.client.rest.HttpException;
import io.linuxserver.fleet.v2.client.rest.RestClient;
import io.linuxserver.fleet.v2.client.rest.RestResponse;
import java.util.ArrayList;
import java.util.List;
public class DockerHubApiClient extends AbstractDockerApiClient {
public static final String DockerHubApiUrl = "https://hub.docker.com/v2";
private static final int DefaultPageSize = 1000;
private final RestClient restClient;
private final IDockerHubAuthenticator authenticator;
public DockerHubApiClient(final RestClient restClient,
final IDockerHubAuthenticator authenticator) {
super(new DockerHubImageConverter(), new DockerHubTagConverter());
this.restClient = Utils.ensureNotNull(restClient);
this.authenticator = Utils.ensureNotNull(authenticator);
}
@Override
public final boolean isRepositoryValid(String repositoryName) {
try {
return !fetchAllImages(repositoryName).isEmpty();
} catch (HttpException e) {
throw new DockerHubException("Unable to verify repository " + repositoryName, e);
}
}
@Override
protected final DockerHubV2Image fetchImageFromApi(String imageName) {
try {
final String absoluteUrl = DockerHubApiUrl + "/repositories/" + imageName + "/";
final RestResponse restResponse = doCall(absoluteUrl, DockerHubV2Image.class);
if (isResponseOK(restResponse)) {
return restResponse.getPayload();
}
return null;
} catch (HttpException e) {
throw new DockerHubException("Unable to get images for " + imageName, e);
}
}
@Override
protected final List fetchAllImagesFromApi(String repositoryName) {
final List images = new ArrayList<>();
try {
String url = DockerHubApiUrl + "/repositories/" + repositoryName + "/?page_size=" + DefaultPageSize;
while (url != null) {
final RestResponse response = doCall(url, DockerHubV2ImageListResult.class);
if (isResponseOK(response)) {
DockerHubV2ImageListResult payload = response.getPayload();
images.addAll(payload.getResults());
url = payload.getNext();
}
}
return images;
} catch (HttpException e) {
throw new DockerHubException("Unable to get images for " + repositoryName, e);
}
}
@Override
protected final List fetchTagsFromApi(String imageName) {
try {
List tags = new ArrayList<>();
String absoluteUrl = DockerHubApiUrl + "/repositories/" + imageName + "/tags/?page_size=" + DefaultPageSize;
while (absoluteUrl != null) {
final RestResponse response = doCall(absoluteUrl, DockerHubV2TagListResult.class);
if (isResponseOK(response)) {
final DockerHubV2TagListResult payload = response.getPayload();
tags.addAll(payload.getResults());
absoluteUrl = payload.getNext();
}
}
return tags;
} catch (HttpException e) {
throw new DockerHubException("Unable to get tags for " + imageName, e);
}
}
/**
*
* Attempts to call DockerHub with the currently set credentials. If they have expired, it will refresh them and try again.
* This process will only try again once, so if the refresh resulted in another stale token, it will need to be handled.
*
*/
private RestResponse doCall(String url, Class responseType) {
RestResponse restResponse = restClient.executeGet(url, null, authenticator.buildAuthHeaders(), responseType);
if (isResponseUnauthorised(restResponse)) {
authenticator.refreshToken();
restResponse = restClient.executeGet(url, null, authenticator.buildAuthHeaders(), responseType);
}
return restResponse;
}
private boolean isResponseOK(final RestResponse> restResponse) {
return restResponse.getStatusCode() == 200;
}
private boolean isResponseUnauthorised(RestResponse restResponse) {
return restResponse.getStatusCode() == 401;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/dockerhub/DockerHubAuthenticator.java
================================================
package io.linuxserver.fleet.v2.client.docker.dockerhub;
import io.linuxserver.fleet.dockerhub.DockerHubException;
import io.linuxserver.fleet.v2.client.rest.RestClient;
import io.linuxserver.fleet.v2.client.rest.RestResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
public class DockerHubAuthenticator implements IDockerHubAuthenticator {
private static final Logger LOGGER = LoggerFactory.getLogger(DockerHubAuthenticator.class);
private final RestClient client;
private final DockerHubCredentials credentials;
private String token;
public DockerHubAuthenticator(final DockerHubCredentials credentials, final RestClient client) {
this.credentials = credentials;
this.client = client;
refreshToken();
}
/**
*
* Re-authenticates with Docker Hub to obtain a fresh JWT.
*
*
* @return
* The new JWT to be used in authenticated requests.
*/
public synchronized String refreshToken() {
LOGGER.info("Refreshing token for Docker Hub authentication");
final RestResponse authenticationResponse = client.executePost(
DockerHubApiClient.DockerHubApiUrl + "/users/login", null, null, credentials, DockerHubTokenResponse.class);
if (authenticationResponse.getStatusCode() == 200) {
LOGGER.info("Refresh successful");
final String token = authenticationResponse.getPayload().getToken();
this.token = token;
return token;
}
LOGGER.info("Unable to refresh token.");
throw new DockerHubException("Unable to authenticate with Docker Hub. Check credentials");
}
synchronized String getCurrentToken() {
if (null == token) {
return refreshToken();
}
return token;
}
@Override
public Map buildAuthHeaders() {
final Map authHeaders = new HashMap<>();
authHeaders.put("Authorization", "JWT " + getCurrentToken());
return authHeaders;
}
static class DockerHubTokenResponse {
private String token;
public String getToken() {
return token;
}
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/dockerhub/DockerHubCredentials.java
================================================
package io.linuxserver.fleet.v2.client.docker.dockerhub;
import io.linuxserver.fleet.v2.Utils;
public class DockerHubCredentials {
private final String username;
private final String password;
public DockerHubCredentials(final String username,
final String password) {
this.username = Utils.ensureNotNull(username);
this.password = Utils.ensureNotNull(password);
}
public final String getUsername() {
return username;
}
public final String getPassword() {
return password;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/dockerhub/DockerHubImageConverter.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker.dockerhub;
import io.linuxserver.fleet.dockerhub.model.DockerHubV2Image;
import io.linuxserver.fleet.v2.client.docker.converter.AbstractDockerResponseConverter;
import io.linuxserver.fleet.v2.types.docker.DockerImage;
public class DockerHubImageConverter extends AbstractDockerResponseConverter {
@Override
protected final DockerImage doPlainConvert(final DockerHubV2Image dockerApiImage) {
return new DockerImage(dockerApiImage.getName(),
dockerApiImage.getNamespace(),
dockerApiImage.getDescription(),
dockerApiImage.getStarCount(),
dockerApiImage.getPullCount(),
parseDockerHubDate(dockerApiImage.getLastUpdated()));
}
@Override
public Class getConverterClass() {
return DockerHubV2Image.class;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/dockerhub/DockerHubTagConverter.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker.dockerhub;
import io.linuxserver.fleet.dockerhub.model.DockerHubV2Tag;
import io.linuxserver.fleet.dockerhub.model.DockerHubV2TagDigest;
import io.linuxserver.fleet.v2.client.docker.converter.AbstractDockerResponseConverter;
import io.linuxserver.fleet.v2.types.docker.DockerTag;
import io.linuxserver.fleet.v2.types.docker.DockerTagManifestDigest;
public class DockerHubTagConverter extends AbstractDockerResponseConverter {
@Override
protected final DockerTag doPlainConvert(final DockerHubV2Tag dockerApiImage) {
final DockerTag dockerTag = new DockerTag(dockerApiImage.getName(),
dockerApiImage.getFullSize(),
parseDockerHubDate(dockerApiImage.getLastUpdated()));
for (DockerHubV2TagDigest tagImageDigest : dockerApiImage.getImages()) {
dockerTag.addDigest(new DockerTagManifestDigest(tagImageDigest.getSize(),
tagImageDigest.getDigest(),
tagImageDigest.getArchitecture(),
tagImageDigest.getVariant()));
}
return dockerTag;
}
@Override
public Class getConverterClass() {
return DockerHubV2Tag.class;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/dockerhub/IDockerHubAuthenticator.java
================================================
package io.linuxserver.fleet.v2.client.docker.dockerhub;
import java.util.Map;
public interface IDockerHubAuthenticator {
Map buildAuthHeaders();
String refreshToken();
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/dockerhub/NoOpDockerHubAuthenticator.java
================================================
package io.linuxserver.fleet.v2.client.docker.dockerhub;
import java.util.HashMap;
import java.util.Map;
public class NoOpDockerHubAuthenticator implements IDockerHubAuthenticator {
@Override
public Map buildAuthHeaders() {
return new HashMap<>();
}
@Override
public String refreshToken() {
return null;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/github/GitHubContainerRegistryClient.java
================================================
/*
* Copyright (c) 2020 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker.github;
import io.linuxserver.fleet.v2.client.docker.AbstractDockerApiClient;
import io.linuxserver.fleet.v2.client.docker.github.model.GitHubImage;
import io.linuxserver.fleet.v2.client.docker.github.model.GitHubTag;
import java.util.List;
public class GitHubContainerRegistryClient extends AbstractDockerApiClient {
public GitHubContainerRegistryClient() {
super(new GitHubImageConverter(), new GitHubTagConverter());
}
@Override
protected final GitHubImage fetchImageFromApi(String imageName) {
return null;
}
@Override
protected final List fetchAllImagesFromApi(String repositoryName) {
return null;
}
@Override
protected final List fetchTagsFromApi(String imageName) {
return null;
}
@Override
public final boolean isRepositoryValid(String repositoryName) {
return false;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/github/GitHubImageConverter.java
================================================
/*
* Copyright (c) 2020 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker.github;
import io.linuxserver.fleet.v2.client.docker.converter.AbstractDockerResponseConverter;
import io.linuxserver.fleet.v2.client.docker.github.model.GitHubImage;
import io.linuxserver.fleet.v2.types.docker.DockerImage;
public class GitHubImageConverter extends AbstractDockerResponseConverter {
@Override
protected final DockerImage doPlainConvert(final GitHubImage dockerApiImage) {
return null;
}
@Override
public final Class getConverterClass() {
return GitHubImage.class;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/github/GitHubTagConverter.java
================================================
/*
* Copyright (c) 2020 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker.github;
import io.linuxserver.fleet.v2.client.docker.converter.AbstractDockerResponseConverter;
import io.linuxserver.fleet.v2.client.docker.github.model.GitHubTag;
import io.linuxserver.fleet.v2.types.docker.DockerTag;
public class GitHubTagConverter extends AbstractDockerResponseConverter {
@Override
protected final DockerTag doPlainConvert(final GitHubTag dockerApiImage) {
return null;
}
@Override
public final Class getConverterClass() {
return GitHubTag.class;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/github/model/GitHubImage.java
================================================
/*
* Copyright (c) 2020 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker.github.model;
public class GitHubImage {
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/github/model/GitHubTag.java
================================================
/*
* Copyright (c) 2020 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker.github.model;
public class GitHubTag {
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/queue/AsyncDockerApiRequest.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker.queue;
import io.linuxserver.fleet.v2.key.ImageKey;
import io.linuxserver.fleet.v2.thread.AsyncTask;
public interface AsyncDockerApiRequest extends AsyncTask {
ImageKey getImageKey();
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/queue/AsyncDockerApiResponse.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker.queue;
import io.linuxserver.fleet.v2.thread.AsyncTaskResponse;
public interface AsyncDockerApiResponse extends AsyncTaskResponse {
void handleDockerApiResponse();
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/queue/DockerApiDelegate.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker.queue;
import io.linuxserver.fleet.core.FleetAppController;
import io.linuxserver.fleet.v2.Utils;
import io.linuxserver.fleet.v2.client.docker.DockerApiClient;
import io.linuxserver.fleet.v2.client.docker.DockerImageNotFoundException;
import io.linuxserver.fleet.v2.key.ImageKey;
import io.linuxserver.fleet.v2.key.RepositoryKey;
import io.linuxserver.fleet.v2.thread.AsyncTaskDelegate;
import io.linuxserver.fleet.v2.types.docker.DockerImage;
import io.linuxserver.fleet.v2.types.docker.DockerTag;
import java.util.List;
public class DockerApiDelegate implements AsyncTaskDelegate {
private final FleetAppController controller;
private final DockerApiClient apiClient;
public DockerApiDelegate(final FleetAppController controller,
final DockerApiClient dockerApiClient) {
this.controller = controller;
this.apiClient = Utils.ensureNotNull(dockerApiClient);
}
public final boolean isRepositoryValid(final String repositoryName) {
return apiClient.isRepositoryValid(repositoryName);
}
public final List getImagesForRepository(final RepositoryKey repositoryKey) {
return apiClient.fetchAllImages(repositoryKey.getName());
}
public final DockerImage getCurrentImageView(final ImageKey imageKey) {
final DockerImage dockerImage = apiClient.fetchImage(imageKey.getAsRepositoryAndImageName());
if (null == dockerImage) {
throw new DockerImageNotFoundException("Image " + imageKey.getAsRepositoryAndImageName() + " was not found upstream.");
}
final List allImageTags = apiClient.fetchImageTags(imageKey.getAsRepositoryAndImageName());
allImageTags.forEach(dockerImage::addTag);
return dockerImage;
}
@Override
public FleetAppController getController() {
return controller;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/queue/DockerApiTaskConsumer.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker.queue;
import io.linuxserver.fleet.v2.service.SynchronisationService;
import io.linuxserver.fleet.v2.thread.AbstractTaskQueueConsumer;
import io.linuxserver.fleet.v2.thread.TaskExecutionException;
public final class DockerApiTaskConsumer extends AbstractTaskQueueConsumer {
public DockerApiTaskConsumer(final SynchronisationService syncService) {
super(syncService.getController(),
syncService.getConfiguredDockerDelegate(),
syncService.getSyncQueue(),
"DockerSyncConsumer");
}
@Override
protected void handleTaskResponse(final DockerImageUpdateResponse response) {
try {
response.handleDockerApiResponse();
} catch (Exception e) {
getLogger().error("handleTaskResponse caught unhandled error, but not something worthy of stalling thread", e);
throw new TaskExecutionException(e);
}
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/queue/DockerImageMissingUpdateResponse.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker.queue;
import io.linuxserver.fleet.core.FleetAppController;
import io.linuxserver.fleet.v2.key.ImageKey;
public class DockerImageMissingUpdateResponse extends DockerImageUpdateResponse {
public DockerImageMissingUpdateResponse(final FleetAppController controller,
final ImageKey imageKey) {
super(controller, imageKey, null);
}
@Override
public final void handleDockerApiResponse() {
// Do nothing. Let schedule handle this.
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/queue/DockerImageUpdateRequest.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker.queue;
import io.linuxserver.fleet.v2.client.docker.DockerImageNotFoundException;
import io.linuxserver.fleet.v2.key.ImageKey;
import io.linuxserver.fleet.v2.thread.AbstractAppTask;
public class DockerImageUpdateRequest extends AbstractAppTask {
private final ImageKey imageKey;
public DockerImageUpdateRequest(final ImageKey imageKey) {
super(imageKey.toString());
this.imageKey = imageKey;
}
@Override
protected DockerImageUpdateResponse performTaskInternal(final DockerApiDelegate delegate) {
try {
return new DockerImageUpdateResponse(delegate.getController(), imageKey, delegate.getCurrentImageView(imageKey));
} catch (DockerImageNotFoundException e) {
getLogger().warn("Request responded with an empty response so assuming image {} has been removed upstream. Error message: {}", imageKey, e.getMessage());
return new DockerImageMissingUpdateResponse(delegate.getController(), imageKey);
}
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/queue/DockerImageUpdateResponse.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker.queue;
import io.linuxserver.fleet.core.FleetAppController;
import io.linuxserver.fleet.v2.key.ImageKey;
import io.linuxserver.fleet.v2.types.docker.DockerImage;
public class DockerImageUpdateResponse implements AsyncDockerApiResponse {
private final FleetAppController controller;
private final ImageKey imageKey;
private final DockerImage latestImage;
public DockerImageUpdateResponse(final FleetAppController controller,
final ImageKey imageKey,
final DockerImage latestImage) {
this.controller = controller;
this.imageKey = imageKey;
this.latestImage = latestImage;
}
protected final FleetAppController getController() {
return controller;
}
@Override
public void handleDockerApiResponse() {
controller.getImageService().applyImageUpstreamUpdate(imageKey, latestImage);
}
@Override
public void handleResponse() {
handleDockerApiResponse();
}
@Override
public String toString() {
return "DockerImageUpdateResponse[" + imageKey.toString() + "]";
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/docker/queue/TaskQueue.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.docker.queue;
import io.linuxserver.fleet.v2.thread.AsyncTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class TaskQueue> {
private static final Logger LOGGER = LoggerFactory.getLogger(TaskQueue.class);
private final BlockingQueue activeTaskQueue;
public TaskQueue() {
activeTaskQueue = new LinkedBlockingQueue<>();
}
public final boolean submitTask(final TASK task) {
LOGGER.info("Task submitted: {}", task);
if (activeTaskQueue.contains(task)) {
LOGGER.warn("Task {} is already queued so will not duplicate the request.", task);
return false;
}
return activeTaskQueue.add(task);
}
public final int size() {
return activeTaskQueue.size();
}
public final TASK retrieveNextTask() throws InterruptedException {
return activeTaskQueue.take();
}
public final boolean isEmpty() {
return activeTaskQueue.isEmpty();
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/rest/HttpException.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.rest;
public class HttpException extends RuntimeException {
public HttpException(String message) {
super(message);
}
public HttpException(String message, Throwable cause) {
super(message, cause);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/rest/RestClient.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.rest;
import io.linuxserver.fleet.v2.client.rest.marshalling.JacksonMarshallingStrategy;
import io.linuxserver.fleet.v2.client.rest.marshalling.MarshallingStrategy;
import io.linuxserver.fleet.v2.client.rest.proxy.LazyLoadPayloadProxy;
import org.apache.http.HttpEntity;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
/**
*
* Simple wrapper for base HttpClient, providing an easier way to marshall/unmarshall payloads
*
*/
public class RestClient {
private static final Logger LOGGER = LoggerFactory.getLogger(RestClient.class);
private final CloseableHttpClient client;
private final HttpClientContext clientContext;
private MarshallingStrategy marshallingStrategy;
public RestClient() {
setMarshallingStrategy(new JacksonMarshallingStrategy());
client = HttpClients.custom().setConnectionManager(new PoolingHttpClientConnectionManager()).build();
clientContext = HttpClientContext.create();
}
private void setMarshallingStrategy(MarshallingStrategy marshallingStrategy) {
LOGGER.debug("Configuring RestClient with " + marshallingStrategy.getClass().getName());
this.marshallingStrategy = marshallingStrategy;
}
public RestResponse executeGet(String url, Map queryParameters, Map headers, Class responseType) {
try {
return executeBaseRequest(responseType, headers, new HttpGet(url + parseQueryParameters(queryParameters)));
} catch (IOException e) {
LOGGER.error("Unable to perform GET", e);
throw new HttpException("Unable to perform GET", e);
}
}
public RestResponse executePost(String url, Map queryParameters, Map headers, Object payload, Class responseType) {
try {
HttpPost post = new HttpPost(url + parseQueryParameters(queryParameters));
post.setEntity(new StringEntity(marshallingStrategy.marshall(payload), StandardCharsets.UTF_8));
post.setHeader("Content-Type", marshallingStrategy.getContentType());
return executeBaseRequest(responseType, headers, post);
} catch (IOException e) {
LOGGER.error("Unable to perform GET", e);
throw new HttpException("Unable to perform GET", e);
}
}
private RestResponse executeBaseRequest(Class responseType, Map headers, HttpRequestBase request) throws IOException {
LOGGER.debug("url : " + request.getURI().toString());
LOGGER.debug("headers : " + headers);
if (headers != null) {
for (Map.Entry header : headers.entrySet())
request.setHeader(header.getKey(), header.getValue());
}
LOGGER.debug("Executing.");
try (CloseableHttpResponse response = client.execute(request, clientContext)) {
StatusLine statusLine = response.getStatusLine();
LOGGER.debug("Response status: " + statusLine);
int statusCode = statusLine.getStatusCode();
HttpEntity content = response.getEntity();
String responseBody = null;
if (null != content) {
responseBody = EntityUtils.toString(content);
LOGGER.debug("Parsed response payload: " + responseBody);
}
if (null != responseBody)
return new RestResponse<>(new LazyLoadPayloadProxy<>(marshallingStrategy, responseBody, responseType), statusCode);
return new RestResponse<>(statusCode);
} finally {
request.releaseConnection();
}
}
private String parseQueryParameters(Map queryParameters) {
if (null != queryParameters) {
StringBuilder builtParameterString = new StringBuilder("?");
for (Map.Entry param : queryParameters.entrySet())
builtParameterString.append(param.getKey()).append("=").append(param.getValue()).append("&");
builtParameterString.setLength(builtParameterString.length() - 1);
return builtParameterString.toString();
}
return "";
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/rest/RestResponse.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.rest;
import io.linuxserver.fleet.v2.client.rest.proxy.PayloadProxy;
public class RestResponse {
private PayloadProxy payloadProxy;
private int statusCode;
RestResponse(int statusCode) {
this(null, statusCode);
}
RestResponse(PayloadProxy payloadProxy, int statusCode) {
this.payloadProxy = payloadProxy;
this.statusCode = statusCode;
}
public T getPayload() {
return payloadProxy.get();
}
public int getStatusCode() {
return statusCode;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/rest/marshalling/JacksonMarshallingStrategy.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.rest.marshalling;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
/**
* Jackson JSON implementation of the marshalling strategy. This will convert incoming
* and outgoing messages formatted in JSON.
*/
public class JacksonMarshallingStrategy implements MarshallingStrategy {
private static final ObjectMapper OBJECT_MAPPER;
static {
OBJECT_MAPPER = new ObjectMapper();
OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
@Override
public T unmarshall(String value, Class classType) throws IOException {
return OBJECT_MAPPER.readValue(value, classType);
}
@Override
public String marshall(Object value) throws JsonProcessingException {
return OBJECT_MAPPER.writeValueAsString(value);
}
@Override
public String getContentType() {
return "application/json";
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/rest/marshalling/MarshallingStrategy.java
================================================
package io.linuxserver.fleet.v2.client.rest.marshalling;
import java.io.IOException;
public interface MarshallingStrategy {
/**
*
* Converts a given string value into its representative object type.
*
* @param value
* The value to convert to an object
* @param classType
* The object class definition
* @return
* The converted object
*/
T unmarshall(String value, Class classType) throws IOException;
/**
*
* Converts an object into a single representative string value.
*
* @param value
* The object to convert
* @return
* The result of the conversion
*/
String marshall(Object value) throws IOException;
/**
*
* The content type of the payloads represented by this strategy.
*
*/
String getContentType();
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/rest/proxy/LazyLoadPayloadProxy.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.rest.proxy;
import io.linuxserver.fleet.v2.client.rest.HttpException;
import io.linuxserver.fleet.v2.client.rest.marshalling.MarshallingStrategy;
import java.io.IOException;
public class LazyLoadPayloadProxy implements PayloadProxy {
private final MarshallingStrategy marshallingStrategy;
private final String payload;
private final Class payloadType;
public LazyLoadPayloadProxy(MarshallingStrategy marshallingStrategy, String payload, Class payloadType) {
this.marshallingStrategy = marshallingStrategy;
this.payload = payload;
this.payloadType = payloadType;
}
@Override
public T get() {
try {
return marshallingStrategy.unmarshall(payload, payloadType);
} catch (IOException e) {
throw new HttpException("Unable to unmarshall response payload", e);
}
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/client/rest/proxy/PayloadProxy.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.client.rest.proxy;
public interface PayloadProxy {
T get();
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/db/AbstractDAO.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.db;
import io.linuxserver.fleet.core.db.DatabaseProvider;
import io.linuxserver.fleet.v2.LoggerOwner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.SQLException;
public class AbstractDAO implements LoggerOwner {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final DatabaseProvider databaseProvider;
public AbstractDAO(final DatabaseProvider databaseProvider) {
this.databaseProvider = databaseProvider;
}
protected final Connection getConnection() throws SQLException {
return databaseProvider.getDatabaseConnection().getConnection();
}
@Override
public final Logger getLogger() {
return logger;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/db/DbUpdateStatus.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.db;
public enum DbUpdateStatus {
Inserted, Updated, NoChange, Exists;
public final boolean isExpected(final DbUpdateStatus expected) {
return this == expected;
}
public final boolean isExists() {
return this == Exists;
}
public final boolean isInserted() {
return this == Inserted;
}
public final boolean isUpdated() {
return this == Updated;
}
public final boolean isNoChange() {
return this == NoChange;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/db/DefaultImageDAO.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.db;
import io.linuxserver.fleet.core.db.DatabaseProvider;
import io.linuxserver.fleet.db.query.InsertUpdateResult;
import io.linuxserver.fleet.db.query.InsertUpdateStatus;
import io.linuxserver.fleet.v2.key.HasKey;
import io.linuxserver.fleet.v2.key.ImageKey;
import io.linuxserver.fleet.v2.key.RepositoryKey;
import io.linuxserver.fleet.v2.key.TagBranchKey;
import io.linuxserver.fleet.v2.types.*;
import io.linuxserver.fleet.v2.types.internal.ImageOutlineRequest;
import io.linuxserver.fleet.v2.types.internal.RepositoryOutlineRequest;
import io.linuxserver.fleet.v2.types.internal.TagBranchOutlineRequest;
import io.linuxserver.fleet.v2.types.meta.*;
import io.linuxserver.fleet.v2.types.meta.history.ImagePullHistory;
import io.linuxserver.fleet.v2.types.meta.history.ImagePullStatistic;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class DefaultImageDAO extends AbstractDAO implements ImageDAO {
private static final String GetRepositoryKeys = "{CALL Repository_GetRepositoryKeys()}";
private static final String GetRepository = "{CALL Repository_Get(?)}";
private static final String DeleteRepository = "{CALL Repository_Delete(?,?)}";
private static final String GetImageKeys = "{CALL Repository_GetImageKeys(?)}";
private static final String CreateRepositoryOutline = "{CALL Repository_CreateOutline(?,?,?,?,?,?,?,?)}";
private static final String StoreRepository = "{CALL Repository_Store(?,?,?,?)}";
private static final String StoreImage = "{CALL Image_Store(?,?,?,?,?,?,?,?,?,?,?)}";
private static final String CreateTagBranchOutline = "{CALL Image_CreateTagBranchOutline(?,?)}";
private static final String RemoveOrphanBranches = "{CALL Image_RemoveOrphanBranches(?,?)}";
private static final String StoreTagBranch = "{CALL Image_StoreTagBranch(?,?,?,?)}";
private static final String StoreTagDigest = "{CALL Image_StoreTagDigest(?,?,?,?,?)}";
private static final String GetTagBranches = "{CALL Image_GetTagBranches(?)}";
private static final String GetTagDigests = "{CALL Image_GetTagDigests(?)}";
private static final String CreateImageOutline = "{CALL Image_CreateOutline(?,?,?,?,?,?,?,?,?,?)}";
private static final String GetImage = "{CALL Image_Get(?)}";
private static final String DeleteImage = "{CALL Image_Delete(?)}";
private static final String GetImageStats = "{CALL Image_GetStats(?)}";
private static final String GetExternalUrls = "{CALL Image_GetExternalUrls(?)}";
private static final String RemoveOrphanUrls = "{CALL Image_RemoveOrphanUrls(?, ?)}";
private static final String StoreCoreMetaData = "{CALL Image_StoreCoreMetaData(?,?,?,?,?)}";
private static final String StoreExternalUrl = "{CALL Image_StoreExternalUrl(?,?,?,?,?)}";
private final ImageTemplateFactory templateFactory;
public DefaultImageDAO(final DatabaseProvider databaseConnection) {
super(databaseConnection);
this.templateFactory = new ImageTemplateFactory();
}
@Override
public Image fetchImage(final ImageKey imageKey) {
try (final Connection connection = getConnection()) {
return makeImage(imageKey, connection);
} catch (SQLException e) {
getLogger().error("Error caught when executing SQL: fetchImage", e);
throw new RuntimeException("fetchImage", e);
}
}
@Override
public InsertUpdateResult storeImage(final Image image) {
try (final Connection connection = getConnection()) {
try (final CallableStatement call = connection.prepareCall(StoreImage)) {
int i = 1;
call.setInt(i++, image.getKey().getId());
call.setLong(i++, image.getPullCount());
call.setInt(i++, image.getStarCount());
Utils.setNullableString(call, i++, image.getDescription());
Utils.setNullableTimestamp(call, i++, image.getLastUpdated());
call.setBoolean(i++, image.isDeprecated());
call.setBoolean(i++, image.isHidden());
call.setBoolean(i++, image.isStable());
call.setBoolean(i++, image.isSyncEnabled());
Utils.setNullableString(call, i++, image.getVersionMask());
call.registerOutParameter(i, Types.VARCHAR);
final ResultSet results = call.executeQuery();
final DbUpdateStatus status = DbUpdateStatus.valueOf(call.getString(i));
if (status.isNoChange()) {
getLogger().warn("storeImage attempted to update an image which did not exist in the database: {}", image);
} else if (results.next()) {
storeTagBranches(connection, image);
return new InsertUpdateResult<>(makeImage(makeImageKey(results), connection));
}
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, "storeImage did not return anything.");
}
} catch (SQLException e) {
getLogger().error("Error caught when executing SQL: storeImage", e);
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, e.getMessage());
}
}
@Override
public InsertUpdateResult storeImageMetaData(final Image image) {
try (final Connection connection = getConnection()) {
storeCoreMetaData(connection, image);
storeExternalUrls(connection, image);
templateFactory.storeImageTemplates(connection, image);
return new InsertUpdateResult<>(makeImage(image.getKey(), connection));
} catch (SQLException e) {
getLogger().error("Error caught when executing SQL: storeImageMetaData", e);
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, e.getMessage());
}
}
@Override
public InsertUpdateResult createImageOutline(final ImageOutlineRequest request) {
try (final Connection connection = getConnection()) {
try (final CallableStatement call = connection.prepareCall(CreateImageOutline)) {
int i = 1;
call.setInt(i++, request.getRepositoryKey().getId());
call.setString(i++, request.getImageName());
call.setString(i++, request.getImageDescription());
Utils.setNullableTimestamp(call, i++, request.getImageLastUpdated());
call.setBoolean(i++, ItemSyncSpec.Default.isDeprecated());
call.setBoolean(i++, ItemSyncSpec.Default.isHidden());
call.setBoolean(i++, ItemSyncSpec.Default.isStable());
call.setBoolean(i++, ItemSyncSpec.Default.isSynchronised());
call.setString(i++, ItemSyncSpec.Default.getVersionMask());
final int statusIndex = i;
call.registerOutParameter(statusIndex, Types.VARCHAR);
final ResultSet results = call.executeQuery();
final DbUpdateStatus status = DbUpdateStatus.valueOf(call.getString(statusIndex));
if (status.isExists()) {
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, "Image already exists");
}
if (results.next()) {
return new InsertUpdateResult<>(makeImage(makeImageKey(results), connection));
}
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, "createImageOutline did not return anything.");
}
} catch (SQLException e) {
getLogger().error("Error caught when executing SQL: createImageOutline", e);
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, e.getMessage());
}
}
@Override
public InsertUpdateResult createTagBranchOutline(final TagBranchOutlineRequest request) {
try (final Connection connection = getConnection()) {
try (final CallableStatement call = connection.prepareCall(CreateTagBranchOutline)) {
int i = 1;
call.setInt(i++, request.getImageKey().getId());
call.setString(i, request.getBranchName());
final ResultSet results = call.executeQuery();
if (results.next()) {
return new InsertUpdateResult<>(makeTagBranch(results, connection, request.getImageKey()));
}
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, "createTagBranchOutline did not return anything.");
}
} catch (SQLException e) {
getLogger().error("Error caught when executing SQL: createTagBranchOutline", e);
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, e.getMessage());
}
}
@Override
public InsertUpdateResult removeImage(final Image image) {
try (final Connection connection = getConnection()) {
try (final CallableStatement call = connection.prepareCall(DeleteImage)) {
call.setInt(1, image.getKey().getId());
call.registerOutParameter(2, Types.VARCHAR);
call.executeUpdate();
final DbUpdateStatus status = DbUpdateStatus.valueOf(call.getString(2));
if (status.isNoChange()) {
getLogger().warn("removeImage attempted to remove an image which did not exist in the database: {}", image);
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, "Unable to remove image " + image);
}
return new InsertUpdateResult<>(null);
}
} catch (SQLException e) {
getLogger().error("Error caught when executing SQL: removeImage", e);
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, e.getMessage());
}
}
@Override
public Repository fetchRepository(final RepositoryKey repositoryKey) {
try (final Connection connection = getConnection()) {
return makeRepository(repositoryKey, connection);
} catch (SQLException e) {
getLogger().error("Error caught when executing SQL: fetchRepository", e);
throw new RuntimeException("fetchRepository", e);
}
}
@Override
public InsertUpdateResult createRepositoryOutline(final RepositoryOutlineRequest request) {
try (final Connection connection = getConnection()) {
try (final CallableStatement call = connection.prepareCall(CreateRepositoryOutline)) {
int i = 1;
call.setString( i++, request.getRepositoryName());
call.setTimestamp(i++, Timestamp.valueOf(LocalDateTime.now()));
call.setBoolean( i++, ItemSyncSpec.Default.isDeprecated());
call.setBoolean( i++, ItemSyncSpec.Default.isHidden());
call.setBoolean( i++, ItemSyncSpec.Default.isStable());
call.setBoolean( i++, ItemSyncSpec.Default.isSynchronised());
call.setString( i++, ItemSyncSpec.Default.getVersionMask());
final int statusIndex = i;
call.registerOutParameter(statusIndex, Types.VARCHAR);
final ResultSet results = call.executeQuery();
final DbUpdateStatus status = DbUpdateStatus.valueOf(call.getString(statusIndex));
if (status.isExists()) {
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, "Repository already exists");
}
if (results.next()) {
return new InsertUpdateResult<>(makeRepository(makeRepositoryKey(results), connection));
}
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, "createRepositoryOutline did not return anything.");
}
} catch (SQLException e) {
getLogger().error("Error caught when executing SQL: createRepositoryOutline", e);
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, e.getMessage());
}
}
@Override
public List fetchAllRepositories() {
try (final Connection connection = getConnection()) {
try (final CallableStatement call = connection.prepareCall(GetRepositoryKeys)) {
final ResultSet results = call.executeQuery();
final List repositoryKeys = new ArrayList<>();
while (results.next()) {
repositoryKeys.add(makeRepositoryKey(results));
}
return makeRepositories(repositoryKeys, connection);
}
} catch (SQLException e) {
getLogger().error("Error caught when executing SQL: fetchAllRepositories", e);
throw new RuntimeException("fetchAllRepositories", e);
}
}
@Override
public InsertUpdateResult storeRepository(Repository repository) {
try (final Connection connection = getConnection()) {
try (final CallableStatement call = connection.prepareCall(StoreRepository)) {
int i = 1;
call.setInt(i++, repository.getKey().getId());
call.setBoolean(i++, repository.getSpec().isSynchronised());
call.setString(i++, repository.getSpec().getVersionMask());
final int statusIndex = i;
call.registerOutParameter(statusIndex, Types.VARCHAR);
call.executeUpdate();
final DbUpdateStatus status = DbUpdateStatus.valueOf(call.getString(statusIndex));
if (status.isUpdated()) {
return new InsertUpdateResult<>(makeRepository(repository.getKey(), connection));
}
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, "Repository was not updated.");
}
} catch (SQLException e) {
getLogger().error("Error caught when executing SQL: storeRepository", e);
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, e.getMessage());
}
}
@Override
public InsertUpdateResult removeRepository(final Repository repository) {
try (final Connection connection = getConnection()) {
try (final CallableStatement call = connection.prepareCall(DeleteRepository)) {
call.setInt(1, repository.getKey().getId());
call.registerOutParameter(2, Types.VARCHAR);
call.executeUpdate();
final DbUpdateStatus status = DbUpdateStatus.valueOf(call.getString(2));
if (status.isNoChange()) {
getLogger().warn("Attempted to delete repository {} but resulted in no change", repository);
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, "Unable to remove repository " + repository.getName());
}
return new InsertUpdateResult<>(null);
}
} catch (SQLException e) {
getLogger().error("Error caught when executing SQL: removeRepository.", e);
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, e.getMessage());
}
}
private void storeCoreMetaData(final Connection connection, final Image image) throws SQLException {
try (final CallableStatement call = connection.prepareCall(StoreCoreMetaData)) {
int i = 1;
call.setInt(i++, image.getKey().getId());
Utils.setNullableString(call, i++, image.getMetaData().getCategory());
Utils.setNullableString(call, i++, image.getMetaData().getBaseImage());
Utils.setNullableString(call, i++, image.getMetaData().getAppImagePath());
final int statusIndex = i;
call.registerOutParameter(statusIndex, Types.VARCHAR);
call.executeUpdate();
final DbUpdateStatus status = DbUpdateStatus.valueOf(call.getString(statusIndex));
getLogger().info("storeCoreMetaData stored with result " + status);
}
}
private void storeExternalUrls(final Connection connection, final Image image) throws SQLException {
final List urls = image.getMetaData().getExternalUrls();
removeOrphans(connection, image.getKey(), urls, RemoveOrphanUrls);
try (final CallableStatement call = connection.prepareCall(StoreExternalUrl)) {
for (ExternalUrl url : urls) {
int i = 1;
call.setInt( i++, image.getKey().getId());
call.setInt( i++, url.getKey().getId());
call.setString(i++, url.getType().name());
call.setString(i++, url.getName());
call.setString(i, url.getAbsoluteUrl());
call.addBatch();
}
call.executeBatch();
}
}
private void storeTagBranches(final Connection connection, final Image image) throws SQLException {
final List tagBranches = image.getTagBranches();
removeOrphans(connection, image.getKey(), tagBranches, RemoveOrphanBranches);
try (final CallableStatement call = connection.prepareCall(StoreTagBranch)) {
for (TagBranch tagBranch : tagBranches) {
int i = 1;
call.setInt(i++, tagBranch.getKey().getImageKey().getId());
call.setInt(i++, tagBranch.getKey().getId());
call.setString(i++, tagBranch.getLatestTag().getVersion());
call.setTimestamp(i, Timestamp.valueOf(tagBranch.getLatestTag().getBuildDate()));
call.addBatch();
}
call.executeBatch();
}
for (TagBranch tagBranch : tagBranches) {
storeTagDigests(connection, tagBranch);
}
}
private void removeOrphans(final Connection connection,
final ImageKey imageKey,
final List extends HasKey>> possibleOrphans,
final String sprocSpec) throws SQLException {
final String ids = possibleOrphans.stream().map(o -> String.valueOf(o.getKey().getId())).collect(Collectors.joining(","));
try (final CallableStatement call = connection.prepareCall(sprocSpec)) {
call.setInt( 1, imageKey.getId());
call.setString(2, ids);
call.executeUpdate();
}
}
private void storeTagDigests(final Connection connection, final TagBranch tagBranch) throws SQLException {
try (final CallableStatement call = connection.prepareCall(StoreTagDigest)) {
for (TagDigest digest : tagBranch.getLatestTag().getDigests()) {
int i = 1;
call.setInt(i++, tagBranch.getKey().getId());
call.setLong(i++, digest.getSize());
call.setString(i++, digest.getDigest());
call.setString(i++, digest.getArchitecture());
call.setString(i, digest.getArchVariant());
call.addBatch();
}
call.executeBatch();
}
}
private List makeRepositories(final List repositoryKeys, final Connection connection) throws SQLException {
final List repositories = new ArrayList<>();
try (final CallableStatement call = connection.prepareCall(GetRepository)) {
for (RepositoryKey key : repositoryKeys) {
call.setInt(1, key.getId());
final Repository repository = makeOneRepository(connection, call);
if (null != repository) {
repositories.add(repository);
} else {
getLogger().warn("makeRepositories attempted to make repository for key {} but none exists. Skipping.", key);
}
}
}
return repositories;
}
private Image makeImage(final ImageKey imageKey, final Connection connection) throws SQLException {
try (final CallableStatement call = connection.prepareCall(GetImage)) {
call.setInt(1, imageKey.getId());
final Image image = makeOneImage(connection, call);
if (null == image) {
getLogger().info("No image with key {} found", imageKey);
}
return image;
}
}
private List makeImages(final List imageKeys, final Connection connection) throws SQLException {
final List images = new ArrayList<>();
try (final CallableStatement call = connection.prepareCall(GetImage)) {
for (ImageKey key : imageKeys) {
call.setInt(1, key.getId());
final Image image = makeOneImage(connection, call);
if (null != image) {
images.add(image);
} else {
getLogger().warn("makeImages attempted to make image for key {} but none exists. Skipping.", key);
}
}
}
return images;
}
private Image makeOneImage(final Connection connection, final CallableStatement call) throws SQLException {
final ResultSet results = call.executeQuery();
if (results.next()) {
final ImageKey imageKey = makeImageKey(results);
final Image image = new Image(imageKey,
makeSyncSpec(results),
makeImageMetaData(connection, imageKey, results),
makeCountData(results),
results.getString("Description"),
results.getTimestamp("LastUpdated").toLocalDateTime());
enrichImageWithTagBranches(image, connection);
return image;
}
return null;
}
private ImageMetaData makeImageMetaData(final Connection connection, final ImageKey imageKey, final ResultSet mainImageResults) throws SQLException {
return new ImageMetaData(makeCoreMeta(connection, imageKey, mainImageResults),
makePullHistory(connection, imageKey),
templateFactory.makeTemplateHolder(connection, imageKey));
}
private ImageCoreMeta makeCoreMeta(final Connection connection, final ImageKey imageKey, final ResultSet mainImageResults) throws SQLException {
final ImageCoreMeta coreMeta = new ImageCoreMeta(mainImageResults.getString("CoreMetaImagePath"),
mainImageResults.getString("CoreMetaBaseImage"),
mainImageResults.getString("CoreMetaCategory"));
makeExternalUrls(connection, imageKey).forEach(coreMeta::addExternalUrl);
return coreMeta;
}
private List makeExternalUrls(final Connection connection, final ImageKey imageKey) throws SQLException {
final List externalUrls = new ArrayList<>();
try (final CallableStatement call = connection.prepareCall(GetExternalUrls)) {
call.setInt(1, imageKey.getId());
final ResultSet results = call.executeQuery();
while (results.next()) {
externalUrls.add(new ExternalUrl(new ExternalUrlKey(results.getInt("UrlId")),
ExternalUrl.ExternalUrlType.valueOf(results.getString("UrlType")),
results.getString("UrlName"),
results.getString("UrlPath")));
}
}
return externalUrls;
}
private ImagePullHistory makePullHistory(final Connection connection, final ImageKey imageKey) throws SQLException {
final ImagePullHistory pullHistory = new ImagePullHistory();
try (final CallableStatement call = connection.prepareCall(GetImageStats)) {
call.setInt(1, imageKey.getId());
final ResultSet results = call.executeQuery();
while (results.next()) {
final long imagePulls = results.getLong("ImagePulls");
if (!results.wasNull()) {
final String timeGroup = results.getString("TimeGroup");
final String groupMode = results.getString("GroupMode");
final ImagePullStatistic statistic = new ImagePullStatistic(imagePulls,
timeGroup,
ImagePullStatistic.StatGroupMode.valueOf(groupMode));
final boolean added = pullHistory.addStatistic(statistic);
if (!added) {
getLogger().warn("Unable to add pull history {} to image {}", statistic, imageKey);
}
}
}
}
return pullHistory;
}
private ImageKey makeImageKey(final ResultSet results) throws SQLException {
return new ImageKey(results.getInt("ImageId"),
results.getString("ImageName"),
makeRepositoryKey(results));
}
private Repository makeRepository(final RepositoryKey repositoryKey, final Connection connection) throws SQLException {
try (final CallableStatement call = connection.prepareCall(GetRepository)) {
call.setInt(1, repositoryKey.getId());
return makeOneRepository(connection, call);
}
}
private Repository makeOneRepository(final Connection connection, final CallableStatement call) throws SQLException {
final ResultSet results = call.executeQuery();
if (results.next()) {
final Repository repository = new Repository(makeRepositoryKey(results),
makeSyncSpec(results));
enrichRepositoryWithImages(repository, connection);
return repository;
}
return null;
}
private void enrichRepositoryWithImages(final Repository repository, final Connection connection) throws SQLException {
try (final CallableStatement call = connection.prepareCall(GetImageKeys)) {
call.setInt(1, repository.getKey().getId());
final List repositoryImageKeys = new ArrayList<>();
final ResultSet results = call.executeQuery();
while (results.next()) {
repositoryImageKeys.add(makeImageKey(results));
}
makeImages(repositoryImageKeys, connection).forEach(repository::addImage);
}
}
private RepositoryKey makeRepositoryKey(final ResultSet results) throws SQLException {
return new RepositoryKey(results.getInt("RepositoryId"),
results.getString("RepositoryName"));
}
private ItemSyncSpec makeSyncSpec(final ResultSet results) throws SQLException {
return new ItemSyncSpec(results.getBoolean("Deprecated"),
results.getBoolean("Hidden"),
results.getBoolean("Stable"),
results.getBoolean("SyncEnabled"),
results.getString("VersionMask"));
}
private ImageCountData makeCountData(final ResultSet results) throws SQLException {
return new ImageCountData(results.getLong("LatestPullCount"),
results.getInt("LatestStarCount"));
}
private void enrichImageWithTagBranches(final Image image, final Connection connection) throws SQLException {
try (final CallableStatement call = connection.prepareCall(GetTagBranches)) {
call.setInt(1, image.getKey().getId());
final ResultSet results = call.executeQuery();
while (results.next()) {
image.addTagBranch(makeTagBranch(results, connection, image.getKey()));
}
}
}
private TagBranch makeTagBranch(final ResultSet results, final Connection connection, final ImageKey imageKey) throws SQLException {
final TagBranchKey tagBranchKey = makeTagBranchKey(results, imageKey);
return new TagBranch(tagBranchKey,
results.getString("BranchName"),
results.getBoolean("BranchProtected"),
makeTag(results, connection, tagBranchKey));
}
private TagBranchKey makeTagBranchKey(ResultSet results, ImageKey imageKey) throws SQLException {
return new TagBranchKey(results.getInt("BranchId"), imageKey);
}
private Tag makeTag(final ResultSet results, final Connection connection, final TagBranchKey tagBranchKey) throws SQLException {
return new Tag(results.getString("TagVersion"),
results.getTimestamp("TagBuildDate").toLocalDateTime(),
makeTagDigests(connection, tagBranchKey));
}
private Set makeTagDigests(final Connection connection, final TagBranchKey tagBranchKey) throws SQLException {
final Set digests = new HashSet<>();
try (final CallableStatement call = connection.prepareCall(GetTagDigests)) {
call.setInt(1, tagBranchKey.getId());
final ResultSet results = call.executeQuery();
while (results.next()) {
digests.add(makeTagDigest(results));
}
}
return digests;
}
private TagDigest makeTagDigest(final ResultSet results) throws SQLException {
return new TagDigest(results.getLong("DigestSize"),
results.getString("DigestSha"),
results.getString("DigestArch"),
results.getString("DigestVariant"));
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/db/DefaultScheduleDAO.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.db;
import io.linuxserver.fleet.core.db.DatabaseProvider;
import io.linuxserver.fleet.v2.key.ScheduleKey;
import io.linuxserver.fleet.v2.thread.schedule.AppSchedule;
import io.linuxserver.fleet.v2.thread.schedule.ScheduleSpec;
import io.linuxserver.fleet.v2.thread.schedule.TimeWithUnit;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;
public class DefaultScheduleDAO extends AbstractDAO implements ScheduleDAO {
private static final String GetScheduleSpecs = "{CALL Schedule_GetSpecs()}";
public DefaultScheduleDAO(final DatabaseProvider databaseProvider) {
super(databaseProvider);
}
@Override
public Set fetchScheduleSpecs() {
final Set specs = new HashSet<>();
try (final Connection connection = getConnection()) {
try (final CallableStatement call = connection.prepareCall(GetScheduleSpecs)) {
final ResultSet results = call.executeQuery();
while (results.next()) {
specs.add(makeOneScheduleSpec(results));
}
} catch (ClassNotFoundException e) {
getLogger().error("No class found for schedule. Ignoring this schedule.", e);
}
} catch (SQLException e) {
getLogger().error("Error caught when executing SQL: fetchScheduleSpecs", e);
throw new RuntimeException("fetchScheduleSpecs", e);
}
return specs;
}
@SuppressWarnings("unchecked")
private ScheduleSpec makeOneScheduleSpec(final ResultSet results) throws SQLException, ClassNotFoundException {
return ScheduleSpec.makeInitial(new ScheduleKey(results.getInt("ScheduleId")),
results.getString("ScheduleName"),
TimeWithUnit.valueOf(results.getString("ScheduleInterval")),
TimeWithUnit.valueOf(results.getString("ScheduleDelayOffset")),
(Class extends AppSchedule>) Class.forName(results.getString("ScheduleClass")));
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/db/DefaultUserDAO.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.db;
import io.linuxserver.fleet.core.db.DatabaseProvider;
import io.linuxserver.fleet.db.query.InsertUpdateResult;
import io.linuxserver.fleet.db.query.InsertUpdateStatus;
import io.linuxserver.fleet.v2.key.UserKey;
import io.linuxserver.fleet.v2.types.User;
import io.linuxserver.fleet.v2.types.internal.UserOutlineRequest;
import io.linuxserver.fleet.v2.web.AppRole;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class DefaultUserDAO extends AbstractDAO implements UserDAO {
private static final String GetUser = "{CALL User_Get(?)}";
private static final String GetUserByName = "{CALL User_GetByName(?)}";
private static final String GetAllUsers = "{CALL User_GetAll()}";
private static final String CreateUser = "{CALL User_CreateOutline(?,?,?,?)}";
private static final String UpdateUser = "{CALL User_Save(?,?,?,?,?)}";
private static final String DeleteUser = "{CALL User_Delete(?)}";
public DefaultUserDAO(DatabaseProvider databaseProvider) {
super(databaseProvider);
}
@Override
public User fetchUser(final UserKey userKey) {
try (final Connection connection = getConnection()) {
try (final CallableStatement call = connection.prepareCall(GetUser)) {
call.setInt(1, userKey.getId());
final ResultSet results = call.executeQuery();
if (results.next()) {
return makeOneUser(results);
}
}
return null;
} catch (SQLException e) {
getLogger().error("fetchAllUsers unable to complete request", e);
throw new RuntimeException("fetchAllUsers", e);
}
}
@Override
public User lookUpUser(final String username) {
try (final Connection connection = getConnection()) {
try (final CallableStatement call = connection.prepareCall(GetUserByName)) {
call.setString(1, username);
final ResultSet results = call.executeQuery();
if (results.next()) {
return makeOneUser(results);
}
}
return null;
} catch (SQLException e) {
getLogger().error("fetchAllUsers unable to complete request", e);
throw new RuntimeException("fetchAllUsers", e);
}
}
@Override
public InsertUpdateResult createUser(final UserOutlineRequest request) {
try (final Connection connection = getConnection()) {
try (final CallableStatement call = connection.prepareCall(CreateUser)) {
int i = 1;
call.setString(i++, request.getUsername());
call.setString(i++, request.getPassword());
call.setString(i++, request.getRole().name());
final int statusIndex = i;
call.registerOutParameter(statusIndex, Types.VARCHAR);
final ResultSet results = call.executeQuery();
final DbUpdateStatus status = DbUpdateStatus.valueOf(call.getString(statusIndex));
if (results.next() && status.isInserted()) {
return new InsertUpdateResult<>(makeOneUser(results));
}
if (status.isExists()) {
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, "User already exists");
}
}
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, "Unknown error");
} catch (SQLException e) {
getLogger().error("createUser unable to complete request", e);
throw new RuntimeException("createUser", e);
}
}
@Override
public List fetchAllUsers() {
final List users = new ArrayList<>();
try (final Connection connection = getConnection()) {
try (final CallableStatement call = connection.prepareCall(GetAllUsers)) {
final ResultSet results = call.executeQuery();
while (results.next()) {
users.add(makeOneUser(results));
}
}
} catch (SQLException e) {
getLogger().error("fetchAllUsers unable to complete request", e);
throw new RuntimeException("fetchAllUsers", e);
}
return users;
}
@Override
public InsertUpdateResult removeUser(final User user) {
try (final Connection connection = getConnection()) {
try (final CallableStatement call = connection.prepareCall(DeleteUser)) {
call.setInt(1, user.getKey().getId());
call.executeUpdate();
}
return new InsertUpdateResult<>(InsertUpdateStatus.OK, "OK");
} catch (SQLException e) {
getLogger().error("updateUser unable to complete request", e);
throw new RuntimeException("updateUser", e);
}
}
@Override
public InsertUpdateResult updateUser(final User updatedUser) {
try (final Connection connection = getConnection()) {
try (final CallableStatement call = connection.prepareCall(UpdateUser)) {
int i = 1;
call.setInt( i++, updatedUser.getKey().getId());
call.setString(i++, updatedUser.getUsername());
call.setString(i++, updatedUser.getPassword());
call.setString(i++, updatedUser.getRole().name());
final int statusIndex = i;
call.registerOutParameter(statusIndex, Types.VARCHAR);
final ResultSet results = call.executeQuery();
final DbUpdateStatus status = DbUpdateStatus.valueOf(call.getString(statusIndex));
if (results.next() && status.isUpdated()) {
return new InsertUpdateResult<>(makeOneUser(results));
}
if (status.isNoChange()) {
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, "No user found");
}
}
return new InsertUpdateResult<>(InsertUpdateStatus.FAILED, "Unknown error");
} catch (SQLException e) {
getLogger().error("updateUser unable to complete request", e);
throw new RuntimeException("updateUser", e);
}
}
private User makeOneUser(final ResultSet results) throws SQLException {
return new User(new UserKey(results.getInt("UserId")),
results.getString("Username"),
results.getString("UserPassword"),
results.getTimestamp("ModifiedTime").toLocalDateTime(),
AppRole.valueOf(results.getString("UserRole")));
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/db/ImageDAO.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.db;
import io.linuxserver.fleet.db.query.InsertUpdateResult;
import io.linuxserver.fleet.v2.key.ImageKey;
import io.linuxserver.fleet.v2.key.RepositoryKey;
import io.linuxserver.fleet.v2.types.Image;
import io.linuxserver.fleet.v2.types.Repository;
import io.linuxserver.fleet.v2.types.TagBranch;
import io.linuxserver.fleet.v2.types.internal.ImageOutlineRequest;
import io.linuxserver.fleet.v2.types.internal.RepositoryOutlineRequest;
import io.linuxserver.fleet.v2.types.internal.TagBranchOutlineRequest;
import java.time.LocalDate;
import java.util.List;
public interface ImageDAO {
Image fetchImage(final ImageKey imageKey);
InsertUpdateResult storeImage(final Image image);
InsertUpdateResult storeImageMetaData(final Image image);
InsertUpdateResult createImageOutline(final ImageOutlineRequest request);
InsertUpdateResult createTagBranchOutline(final TagBranchOutlineRequest request);
InsertUpdateResult removeImage(final Image image);
Repository fetchRepository(final RepositoryKey repositoryKey);
InsertUpdateResult createRepositoryOutline(final RepositoryOutlineRequest request);
List fetchAllRepositories();
InsertUpdateResult storeRepository(Repository repository);
InsertUpdateResult removeRepository(final Repository repository);
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/db/ImageTemplateFactory.java
================================================
/*
* Copyright (c) 2020 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.db;
import io.linuxserver.fleet.v2.key.ImageKey;
import io.linuxserver.fleet.v2.types.Image;
import io.linuxserver.fleet.v2.types.docker.DockerCapability;
import io.linuxserver.fleet.v2.types.meta.template.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.*;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
public class ImageTemplateFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(ImageTemplateFactory.class);
private static final String ClearTemplateData = "{CALL Image_ClearTemplateData(?,?,?,?,?,?)}";
private static final String StoreTemplateBase = "{CALL Image_StoreTemplateBase(?,?,?,?,?,?)}";
private static final String StoreTemplatePort = "{CALL Image_StoreTemplatePort(?,?,?,?)}";
private static final String StoreTemplateVolume = "{CALL Image_StoreTemplateVolume(?,?,?,?)}";
private static final String StoreTemplateEnv = "{CALL Image_StoreTemplateEnv(?,?,?,?)}";
private static final String StoreTemplateDevice = "{CALL Image_StoreTemplateDevice(?,?,?)}";
private static final String StoreTemplateExtra = "{CALL Image_StoreTemplateExtra(?,?,?)}";
private static final String GetImageTemplateBase = "{CALL Image_GetTemplateBase(?)}";
private static final String GetImageTemplates = "{CALL Image_GetTemplates(?)}";
public final ImageTemplateHolder makeTemplateHolder(final Connection connection, final ImageKey imageKey) throws SQLException {
String registryUrl = null;
String restartPolicy = null;
boolean hostNetworkingEnabled = false;
boolean privilegedMode = false;
try (final CallableStatement call = connection.prepareCall(GetImageTemplateBase)) {
call.setInt(1, imageKey.getId());
final ResultSet results = call.executeQuery();
if (results.next()) {
registryUrl = results.getString("RepositoryUrl");
restartPolicy = results.getString("RestartPolicy");
hostNetworkingEnabled = results.getBoolean("HostNetworkEnabled");
privilegedMode = results.getBoolean("PrivilegedMode");
}
}
final ImageTemplateHolder templateHolder = new ImageTemplateHolder(registryUrl,
restartPolicy,
hostNetworkingEnabled,
privilegedMode);
enrichHolderWithTemplates(connection, imageKey, templateHolder);
return templateHolder;
}
public final void storeImageTemplates(final Connection connection, final Image image) throws SQLException {
CallableStatement clearTemplatesCall = null;
CallableStatement storePortCall = null;
CallableStatement storeVolumeCall = null;
CallableStatement storeEnvCall = null;
CallableStatement storeDeviceCall = null;
CallableStatement storeCapabilityCall = null;
CallableStatement storeBaseCall = null;
try {
connection.setAutoCommit(false);
clearTemplatesCall = connection.prepareCall(ClearTemplateData);
storeBaseCall = connection.prepareCall(StoreTemplateBase);
storePortCall = connection.prepareCall(StoreTemplatePort);
storeVolumeCall = connection.prepareCall(StoreTemplateVolume);
storeEnvCall = connection.prepareCall(StoreTemplateEnv);
storeDeviceCall = connection.prepareCall(StoreTemplateDevice);
storeCapabilityCall = connection.prepareCall(StoreTemplateExtra);
clearTemplates( clearTemplatesCall, image);
storeTemplateBase( storeBaseCall, image);
storeTemplatePorts( storePortCall, image);
storeTemplateVolumes( storeVolumeCall, image);
storeTemplateEnv( storeEnvCall, image);
storeTemplateDevices( storeDeviceCall, image);
storeTemplateCapabilities(storeCapabilityCall, image);
connection.commit();
} catch (SQLException e) {
LOGGER.error("storeImageTemplates unable to complete transaction, rolling back", e);
connection.rollback();
throw new SQLException(e);
} finally {
Utils.safeClose(clearTemplatesCall);
Utils.safeClose(storePortCall);
Utils.safeClose(storeVolumeCall);
Utils.safeClose(storeEnvCall);
Utils.safeClose(storeDeviceCall);
Utils.safeClose(storeCapabilityCall);
connection.setAutoCommit(true);
}
}
private void enrichHolderWithTemplates(final Connection connection, final ImageKey imageKey, final ImageTemplateHolder templateHolder) throws SQLException {
try (final CallableStatement call = connection.prepareCall(GetImageTemplates)) {
call.setInt(1, imageKey.getId());
final ResultSet results = call.executeQuery();
while (results.next()) {
final String itemType = results.getString("ItemType");
final String itemName = results.getString("ItemName");
final String itemDesc = results.getString("ItemDescription");
final String itemSec = results.getString("ItemSecondary");
switch (itemType) {
case "Port":
templateHolder.addPort(new PortTemplateItem(Integer.parseInt(itemName), itemDesc, PortTemplateItem.Protocol.fromName(itemSec)));
break;
case "Volume":
templateHolder.addVolume(new VolumeTemplateItem(itemName, itemDesc, "1".equalsIgnoreCase(itemSec)));
break;
case "Env":
templateHolder.addEnvironment(new EnvironmentTemplateItem(itemName, itemDesc, itemSec));
break;
case "Device":
templateHolder.addDevice(new DeviceTemplateItem(itemName, itemDesc));
break;
case "Extra":
templateHolder.addCapability(DockerCapability.valueOf(itemName));
break;
default:
LOGGER.warn("Found unknown template type " + itemType);
}
}
}
}
private void clearTemplates(final CallableStatement clearTemplatesCall, final Image image) throws SQLException {
final String volumes = joinStream(image.getMetaData().getTemplates().getVolumes(), VolumeTemplateItem::getVolume);
final String ports = joinStream(image.getMetaData().getTemplates().getPorts(), (p) -> String.valueOf(p.getPort()));
final String env = joinStream(image.getMetaData().getTemplates().getEnv(), EnvironmentTemplateItem::getEnv);
final String caps = joinStream(image.getMetaData().getTemplates().getCapabilities(), DockerCapability::name);
final String devices = joinStream(image.getMetaData().getTemplates().getDevices(), DeviceTemplateItem::getDevice);
int i = 1;
clearTemplatesCall.setInt( i++, image.getKey().getId());
clearTemplatesCall.setString(i++, volumes);
clearTemplatesCall.setString(i++, ports);
clearTemplatesCall.setString(i++, env);
clearTemplatesCall.setString(i++, caps);
clearTemplatesCall.setString(i, devices);
clearTemplatesCall.executeUpdate();
}
private String joinStream(final List list, final Function super T, ? extends R> getter) {
return list.stream().map(getter).collect(Collectors.joining(","));
}
private void storeTemplateBase(final CallableStatement storeBaseCall, final Image image) throws SQLException {
int i = 1;
storeBaseCall.setInt(i++, image.getKey().getId());
storeBaseCall.setString(i++, image.getMetaData().getTemplates().getRegistryUrl());
storeBaseCall.setString(i++, image.getMetaData().getTemplates().getRestartPolicy());
storeBaseCall.setBoolean(i++, image.getMetaData().getTemplates().isHostNetworkingEnabled());
storeBaseCall.setBoolean(i++, image.getMetaData().getTemplates().isPrivilegedMode());
final int statusIndex = i;
storeBaseCall.registerOutParameter(i, Types.VARCHAR);
storeBaseCall.executeUpdate();
final DbUpdateStatus status = DbUpdateStatus.valueOf(storeBaseCall.getString(statusIndex));
LOGGER.info("storeTemplateBase update response=" + status);
}
private void storeTemplateCapabilities(final CallableStatement storeCapabilityCall, final Image image) throws SQLException {
for (DockerCapability cap : image.getMetaData().getTemplates().getCapabilities()) {
int i = 1;
storeCapabilityCall.setInt( i++, image.getKey().getId());
storeCapabilityCall.setString(i++, cap.name());
storeCapabilityCall.setNull( i, Types.VARCHAR);
storeCapabilityCall.addBatch();
}
storeCapabilityCall.executeBatch();
}
private void storeTemplateDevices(final CallableStatement storeDeviceCall, final Image image) throws SQLException {
for (DeviceTemplateItem device : image.getMetaData().getTemplates().getDevices()) {
int i = 1;
storeDeviceCall.setInt( i++, image.getKey().getId());
storeDeviceCall.setString(i++, device.getDevice());
storeDeviceCall.setString(i, device.getDescription());
storeDeviceCall.addBatch();
}
storeDeviceCall.executeBatch();
}
private void storeTemplateEnv(final CallableStatement storeEnvCall, final Image image) throws SQLException {
for (EnvironmentTemplateItem env : image.getMetaData().getTemplates().getEnv()) {
int i = 1;
storeEnvCall.setInt( i++, image.getKey().getId());
storeEnvCall.setString(i++, env.getEnv());
storeEnvCall.setString(i++, env.getDescription());
storeEnvCall.setString(i, env.getExampleValue());
storeEnvCall.addBatch();
}
storeEnvCall.executeBatch();
}
private void storeTemplateVolumes(final CallableStatement storeVolumeCall, final Image image) throws SQLException {
for (VolumeTemplateItem volume : image.getMetaData().getTemplates().getVolumes()) {
int i = 1;
storeVolumeCall.setInt( i++, image.getKey().getId());
storeVolumeCall.setString( i++, volume.getVolume());
storeVolumeCall.setString( i++, volume.getDescription());
storeVolumeCall.setBoolean(i, volume.isReadonly());
storeVolumeCall.addBatch();
}
storeVolumeCall.executeBatch();
}
private void storeTemplatePorts(final CallableStatement storePortCall, final Image image) throws SQLException {
for (PortTemplateItem port : image.getMetaData().getTemplates().getPorts()) {
int i = 1;
storePortCall.setInt( i++, image.getKey().getId());
storePortCall.setInt( i++, port.getPort());
storePortCall.setString(i++, port.getDescription());
storePortCall.setString(i, port.getProtocol());
storePortCall.addBatch();
}
storePortCall.executeBatch();
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/db/ScheduleDAO.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.db;
import io.linuxserver.fleet.v2.thread.schedule.ScheduleSpec;
import java.util.Set;
public interface ScheduleDAO {
Set fetchScheduleSpecs();
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/db/UserDAO.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.db;
import io.linuxserver.fleet.db.query.InsertUpdateResult;
import io.linuxserver.fleet.v2.key.UserKey;
import io.linuxserver.fleet.v2.types.User;
import io.linuxserver.fleet.v2.types.internal.UserOutlineRequest;
import java.util.List;
public interface UserDAO {
User fetchUser(UserKey userKey);
User lookUpUser(String username);
InsertUpdateResult createUser(UserOutlineRequest request);
List fetchAllUsers();
InsertUpdateResult removeUser(User user);
InsertUpdateResult updateUser(User updatedUser);
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/db/Utils.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.db;
import java.sql.CallableStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.LocalDateTime;
class Utils {
static void setNullableInt(CallableStatement call, int position, Integer value) throws SQLException {
if (null == value)
call.setNull(position, Types.INTEGER);
else
call.setInt(position, value);
}
static void setNullableLong(CallableStatement call, int position, Long value) throws SQLException {
if (null == value)
call.setNull(position, Types.BIGINT);
else
call.setLong(position, value);
}
static void setNullableString(CallableStatement call, int position, String value) throws SQLException {
if (null == value)
call.setNull(position, Types.VARCHAR);
else
call.setString(position, value);
}
static void setNullableTimestamp(CallableStatement call, int position, LocalDateTime localDateTime) throws SQLException {
if (null == localDateTime) {
call.setNull(position, Types.TIMESTAMP);
} else {
call.setTimestamp(position, Timestamp.valueOf(localDateTime));
}
}
static void safeClose(CallableStatement call) {
try {
if (null != call)
call.close();
} catch (SQLException e) {
throw new RuntimeException("Unable to close call", e);
}
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/file/FileManager.java
================================================
/*
* Copyright (c) 2020 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.file;
import io.linuxserver.fleet.core.FleetAppController;
import io.linuxserver.fleet.v2.key.ImageKey;
import io.linuxserver.fleet.v2.service.AbstractAppService;
import io.linuxserver.fleet.v2.types.FilePathDetails;
import io.linuxserver.fleet.v2.types.internal.ImageAppLogo;
import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
public class FileManager extends AbstractAppService {
private static final String imageDirName = "images";
private final String publicSafeImagesDir;
private final Path staticImagesDir;
public FileManager(final FleetAppController controller) {
super(controller);
staticImagesDir = Paths.get(getProperties().getStaticFilesPath().toString(), imageDirName);
publicSafeImagesDir = "/" + imageDirName;
makeImageUploadDir();
}
public final FilePathDetails saveImageLogo(final ImageAppLogo logo) {
if (logo.getMimeType().startsWith("image/")) {
try {
final FilePathDetails filePathDetails = makeFilePathDetails(logo);
final File logoFile = new File(filePathDetails.getFullAbsolutePathWithFileName());
if (logoFile.exists()) {
final boolean deleted = logoFile.delete();
if (!deleted) {
getLogger().warn("Unable to delete file: " + logoFile);
return null;
}
}
boolean created = logoFile.createNewFile();
if (created) {
writeDataToFile(logo, logoFile);
return filePathDetails;
} else {
getLogger().warn("Unable to delete file: " + logoFile);
return null;
}
} catch (IOException e) {
getLogger().error("Unable to create logo file.", e);
throw new RuntimeException(e);
}
} else {
throw new IllegalArgumentException("Disallowed mimeType for file: " + logo.getMimeType());
}
}
private FilePathDetails makeFilePathDetails(final ImageAppLogo logo) {
return new FilePathDetails(makePathSafeFileName(logo.getImageKey()) + logo.getFileExtension(),
staticImagesDir.toString(),
publicSafeImagesDir);
}
private String makePathSafeFileName(final ImageKey key) {
return key.getAsRepositoryAndImageName().replace("/", "_");
}
private void writeDataToFile(final ImageAppLogo logo, final File logoFile) throws IOException {
try (final InputStream initialStream = logo.getRawDataStream();
final OutputStream out = new FileOutputStream(logoFile)) {
byte[] buffer = new byte[8 * 1024];
int bytesRead;
while ((bytesRead = initialStream.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}
}
private void makeImageUploadDir() {
final File imageDir = new File(staticImagesDir.toString());
if (!imageDir.exists()) {
getLogger().info("Creating new image directory for uploaded logos");
final boolean created = imageDir.mkdir();
if (!created) {
throw new RuntimeException("Unable to create uploaded file dir. Check permissions");
}
}
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/key/AbstractDatabaseKey.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.key;
public abstract class AbstractDatabaseKey implements Key {
private final Integer id;
public AbstractDatabaseKey(final Integer id) {
this.id = id;
}
@Override
public final Integer getId() {
return id;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Key)) {
return false;
}
if (null == id) {
return ((Key) o).getId() == null;
}
return ((Key) o).getId().equals(id);
}
@Override
public int hashCode() {
if (null == id) {
return -1;
}
return id.hashCode();
}
@Override
public String toString() {
return null == id ? "" : String.valueOf(id);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/key/AbstractHasKey.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.key;
import io.linuxserver.fleet.v2.Utils;
public abstract class AbstractHasKey implements HasKey {
private final KEY key;
public AbstractHasKey(final KEY key) {
this.key = Utils.ensureNotNull(key);
}
@Override
public final KEY getKey() {
return key;
}
@Override
public int hashCode() {
return key.hashCode();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof HasKey>)) {
return false;
}
return key.equals(((HasKey>) o).getKey());
}
@Override
public String toString() {
return key.toString();
}
@Override
public int compareTo(HasKey o) {
if (null == o) {
return -1;
}
final Integer otherId = o.getKey().getId();
final Integer thisId = getKey().getId();
if (null == otherId && null == thisId) {
return 0;
}
if (null == otherId) {
return -1;
} else {
return o.getKey().getId().compareTo(getKey().getId());
}
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/key/AbstractLookupKey.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.key;
public abstract class AbstractLookupKey> implements Key {
private final String query;
public AbstractLookupKey(final String query) {
this.query = query;
}
public final String getQuery() {
return query;
}
@Override
public final Integer getId() {
return null;
}
@Override
public String toString() {
return query;
}
public abstract boolean isLookupKeyFor(final TYPE type);
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/key/AlertKey.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.key;
import io.linuxserver.fleet.v2.types.AppAlert;
public class AlertKey extends AbstractLookupKey {
public AlertKey(String query) {
super(query);
}
@Override
public boolean isLookupKeyFor(final AppAlert appAlert) {
throw new RuntimeException("Operation not supported");
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/key/HasKey.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.key;
public interface HasKey extends Comparable> {
KEY getKey();
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/key/ImageKey.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.key;
public class ImageKey extends AbstractDatabaseKey {
private static final String KeyPattern = "^\\d+:\\d+:[^/]+/[^/]+$";
private final RepositoryKey repositoryKey;
private final String name;
@Deprecated
public ImageKey(final String name, final RepositoryKey repositoryKey) {
this(null, name, repositoryKey);
}
public ImageKey(final Integer id, final String name, final RepositoryKey repositoryKey) {
super(id);
this.repositoryKey = repositoryKey;
this.name = name;
}
public static ImageKey parse(final String keyAsString) {
if (keyAsString.matches(KeyPattern)) {
final String[] keyParts = keyAsString.split(":");
final String[] names = keyParts[2].split("/");
final int repositoryId = Integer.parseInt(keyParts[0]);
final int imageId = Integer.parseInt(keyParts[1]);
final String repositoryName = names[0];
final String imageName = names[1];
return new ImageKey(imageId, imageName, new RepositoryKey(repositoryId, repositoryName));
} else {
throw new IllegalArgumentException("Key pattern is malformed");
}
}
public final ImageLookupKey getAsLookupKey() {
return new ImageLookupKey(getRepositoryKey().getName() + "/" + getName());
}
public final String getAsRepositoryAndImageName() {
return getAsLookupKey().toString();
}
public final RepositoryKey getRepositoryKey() {
return repositoryKey;
}
public final String getName() {
return name;
}
@Override
public String toString() {
return repositoryKey.getId() + ":" + super.toString() + ":" + repositoryKey.getName() + "/" + name;
}
@Override
public boolean equals(Object o) {
return super.equals(o) && ((ImageKey) o).name.equals(name);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/key/ImageLookupKey.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.key;
import io.linuxserver.fleet.v2.types.Image;
public class ImageLookupKey extends AbstractLookupKey {
private static final String KeyPattern = "^[^/]+/[^/]+$";
private final String lookupRepositoryName;
private final String lookupImageName;
public ImageLookupKey(final String query) {
super(query);
if (query.matches(KeyPattern)) {
final String[] names = query.split("/");
lookupRepositoryName = names[0];
lookupImageName = names[1];
} else {
throw new IllegalArgumentException("Malformed lookup query for ImageLookupKey");
}
}
@Override
public final boolean isLookupKeyFor(final Image image) {
if (null == image) {
return false;
}
return image.getRepositoryName().equals(lookupRepositoryName) && image.getName().equals(lookupImageName);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/key/Key.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.key;
import javax.naming.OperationNotSupportedException;
public interface Key {
Integer getId();
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/key/RepositoryKey.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.key;
public class RepositoryKey extends AbstractDatabaseKey {
private static final String KeyPattern = "^\\d+:[^/]++$";
private final String name;
public static RepositoryKey parse(final String keyAsString) {
if (keyAsString.matches(KeyPattern)) {
final String[] keyParts = keyAsString.split(":");
final int repositoryId = Integer.parseInt(keyParts[0]);
final String repositoryName = keyParts[1];
return new RepositoryKey(repositoryId, repositoryName);
} else {
throw new IllegalArgumentException("Key pattern is malformed");
}
}
public RepositoryKey(final Integer id, final String name) {
super(id);
this.name = name;
}
public RepositoryKey cloneWithId(int id) {
return new RepositoryKey(id, name);
}
public final String getName() {
return name;
}
@Override
public String toString() {
return super.toString() + ":" + name;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/key/ScheduleKey.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.key;
public class ScheduleKey extends AbstractDatabaseKey {
public ScheduleKey(Integer id) {
super(id);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/key/TagBranchKey.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.key;
public class TagBranchKey extends AbstractDatabaseKey {
private final ImageKey imageKey;
public TagBranchKey(final Integer id, final ImageKey imageKey) {
super(id);
this.imageKey = imageKey;
}
public final ImageKey getImageKey() {
return imageKey;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/key/UserKey.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.key;
public class UserKey extends AbstractDatabaseKey {
public UserKey() {
this(null);
}
public UserKey(Integer id) {
super(id);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/service/AbstractAppService.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.service;
import io.linuxserver.fleet.core.FleetAppController;
import io.linuxserver.fleet.core.config.AppProperties;
import io.linuxserver.fleet.v2.LoggerOwner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AbstractAppService implements LoggerOwner {
private final Logger logger;
private final FleetAppController controller;
public AbstractAppService(FleetAppController controller) {
this.controller = controller;
this.logger = LoggerFactory.getLogger(getClass());
}
public final FleetAppController getController() {
return controller;
}
public final AppProperties getProperties() {
return getController().getAppProperties();
}
public final Logger getLogger() {
return logger;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/service/ImageService.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.service;
import io.linuxserver.fleet.core.FleetAppController;
import io.linuxserver.fleet.db.query.InsertUpdateResult;
import io.linuxserver.fleet.dockerhub.util.DockerTagFinder;
import io.linuxserver.fleet.v2.cache.RepositoryCache;
import io.linuxserver.fleet.v2.db.ImageDAO;
import io.linuxserver.fleet.v2.file.FileManager;
import io.linuxserver.fleet.v2.key.ImageKey;
import io.linuxserver.fleet.v2.key.ImageLookupKey;
import io.linuxserver.fleet.v2.key.RepositoryKey;
import io.linuxserver.fleet.v2.service.util.TemplateMerger;
import io.linuxserver.fleet.v2.types.*;
import io.linuxserver.fleet.v2.types.docker.DockerImage;
import io.linuxserver.fleet.v2.types.docker.DockerTag;
import io.linuxserver.fleet.v2.types.internal.*;
import io.linuxserver.fleet.v2.types.meta.ImageCoreMeta;
import io.linuxserver.fleet.v2.types.meta.ImageMetaData;
import io.linuxserver.fleet.v2.types.meta.ItemSyncSpec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class ImageService extends AbstractAppService {
private static final Logger LOGGER = LoggerFactory.getLogger(ImageService.class);
private final ImageDAO imageDAO;
private final FileManager fileManager;
private final RepositoryCache repositoryCache;
private final TemplateMerger templateMerger;
public ImageService(final FleetAppController controller, final ImageDAO imageDAO) {
super(controller);
this.imageDAO = imageDAO;
this.fileManager = controller.getFileManager();
this.repositoryCache = new RepositoryCache();
this.templateMerger = new TemplateMerger();
reloadCache();
}
public final void reloadCache() {
final List allItems = imageDAO.fetchAllRepositories();
repositoryCache.clear();
repositoryCache.addAllItems(allItems);
}
public final Image updateImageSpec(final ImageKey imageKey, final ItemSyncSpec updatedSpec) {
final Image cachedImage = getImage(imageKey);
if (null == cachedImage) {
throw new IllegalArgumentException("Image key " + imageKey + " did not match known image");
}
final Image updated = cachedImage.cloneWithSyncSpec(updatedSpec);
return storeImage(updated);
}
public final Repository updateRepositorySpec(final RepositoryKey repositoryKey, final ItemSyncSpec updatedSpec) {
final Repository cachedRepository = getRepository(repositoryKey);
if (null == cachedRepository) {
throw new IllegalArgumentException("Repository key " + repositoryKey + " did not match known repository");
}
final Repository updatedRepository = cachedRepository.cloneWithSyncSpec(updatedSpec);
final InsertUpdateResult result = imageDAO.storeRepository(updatedRepository);
if (result.isError()) {
LOGGER.error("Unable to store repository {}. Update returned error: {}", repositoryKey, result.getStatusMessage());
throw new RuntimeException("Failed to store repository: " + result.getStatusMessage());
}
final Repository storedRepository = result.getResult();
repositoryCache.addItem(storedRepository);
return storedRepository;
}
public Repository createRepositoryOutline(final RepositoryOutlineRequest repositoryOutlineRequest) {
final InsertUpdateResult result = imageDAO.createRepositoryOutline(repositoryOutlineRequest);
if (result.isError()) {
LOGGER.error("Unable to create repository outline {}, reason: {}", repositoryOutlineRequest, result.getStatusMessage());
throw new RuntimeException("Unable to create repository outline");
}
final Repository repositoryOutline = result.getResult();
LOGGER.info("Successfully created outline for repository");
repositoryCache.addItem(repositoryOutline);
return repositoryOutline;
}
public final Image createImageOutline(final ImageOutlineRequest request) {
final InsertUpdateResult result = imageDAO.createImageOutline(request);
if (result.isError()) {
LOGGER.error("Unable to create image outline {}, reason: {}", request, result.getStatusMessage());
throw new RuntimeException("Unable to create outline");
}
final Image imageOutline = result.getResult();
LOGGER.info("Successfully created outline for image {}", imageOutline);
updateCache(imageOutline);
return imageOutline;
}
public final void removeImage(final ImageKey imageKey) {
final Image cachedImage = findImage(imageKey);
final InsertUpdateResult removalResult = imageDAO.removeImage(cachedImage);
if (removalResult.isError()) {
throw new RuntimeException("Unable to remove persisted image: " + removalResult.getStatusMessage());
}
repositoryCache.findItem(cachedImage.getRepositoryKey()).removeImage(cachedImage);
}
public final void removeRepository(final RepositoryKey repositoryKey) {
if (repositoryCache.isItemCached(repositoryKey)) {
final Repository cached = repositoryCache.findItem(repositoryKey);
final InsertUpdateResult removalResult = imageDAO.removeRepository(cached);
if (removalResult.isError()) {
throw new RuntimeException("Unable to remove repository " + repositoryKey);
}
repositoryCache.removeItem(cached.getKey());
} else {
throw new IllegalArgumentException("Unable to find cached repository " + repositoryKey);
}
}
public final Image storeImage(final Image image) {
return storeImage(image, imageDAO::storeImage);
}
public final Image storeImageTemplateMetaData(final Image image) {
return storeImage(image, imageDAO::storeImageMetaData);
}
public final Image getImage(final ImageKey imageKey) {
return repositoryCache.findImage(imageKey);
}
public final Image lookupImage(final ImageLookupKey imageLookupKey) {
return repositoryCache.lookupImage(imageLookupKey);
}
public final Repository getRepository(final RepositoryKey repositoryKey) {
return repositoryCache.findItem(repositoryKey);
}
public final Repository getFirstRepository() {
return getAllShownRepositories().stream().findFirst().orElse(null);
}
public final List getAllRepositories() {
return new ArrayList<>(repositoryCache.getAllItems());
}
public final List getAllShownRepositories() {
return getAllRepositories().stream().filter(r -> !r.isHidden()).collect(Collectors.toList());
}
public Image applyImageUpstreamUpdate(final ImageKey imageKey, final DockerImage latestImage) {
final Image cachedImage = findImage(imageKey);
final Image cloned = cachedImage.cloneForUpdate(latestImage.getPullCount(),
latestImage.getStarCount(),
latestImage.getDescription(),
latestImage.getBuildDate());
for (TagBranch branch : cloned.getTagBranches()) {
final DockerTag matchingTag = DockerTagFinder.findVersionedTagMatchingBranch(latestImage.getTags(), branch.getBranchName());
if (null == matchingTag) {
LOGGER.warn("Unable to find tag for branch {} in image {}. Will not update tags.", branch.getBranchName(), cloned.getFullName());
} else {
branch.updateLatestTag(new Tag(matchingTag.getName(),
matchingTag.getBuildDate(),
matchingTag.getDigests().stream()
.filter(Objects::nonNull)
.map(d -> new TagDigest(d.getSize(),
d.getDigest(),
d.getArchitecture(),
d.getArchVariant())).collect(Collectors.toSet())));
}
}
return storeImage(cloned);
}
public void trackBranchOnImage(final ImageKey imageKey, final String branchName) {
final Image image = findImage(imageKey);
if (image.findTagBranchByName(branchName) != null) {
throw new IllegalArgumentException("Image is already tracking branch " + branchName);
}
final InsertUpdateResult outlineResult = imageDAO.createTagBranchOutline(new TagBranchOutlineRequest(imageKey, branchName));
if (outlineResult.isError()) {
throw new RuntimeException(outlineResult.getStatusMessage());
}
final Image updatableClone = image.cloneForUpdate();
updatableClone.addTagBranch(outlineResult.getResult());
storeImage(updatableClone);
}
public void removeTrackedBranch(final ImageKey imageKey, final String branchName) {
final Image image = findImage(imageKey);
final TagBranch branch = image.findTagBranchByName(branchName);
if (branch == null) {
throw new IllegalArgumentException("Could not find " + branchName);
}
final Image updatableClone = image.cloneForUpdate();
updatableClone.removeTagBranch(branch);
storeImage(updatableClone);
}
public void updateImageGeneralInfo(final ImageKey imageKey, final ImageGeneralInfoUpdateRequest generalInfoUpdateRequest) {
final Image image = findImage(imageKey);
final ImageMetaData metaData = image.getMetaData();
String appLogoPath = metaData.getAppImagePath();
if (null != generalInfoUpdateRequest.getImageAppLogo()) {
final FilePathDetails filePathDetails = fileManager.saveImageLogo(generalInfoUpdateRequest.getImageAppLogo());
if (null != filePathDetails) {
appLogoPath = filePathDetails.getPublicSafePathWithFileName();
}
}
final ImageCoreMeta coreMeta = metaData.getCoreMeta().cloneWithBaseData(appLogoPath,
generalInfoUpdateRequest.getBaseImage(),
generalInfoUpdateRequest.getCategory());
final Image cloned = image.cloneWithMetaData(metaData.cloneWithCoreMeta(coreMeta));
storeImageTemplateMetaData(cloned);
}
public void updateImageExternalUrls(final ImageKey imageKey, final ImageUrlsUpdateRequest imageUrlsUpdateFields) {
final Image image = findImage(imageKey);
final ImageMetaData metaData = image.getMetaData();
final ImageCoreMeta coreMeta = metaData.getCoreMeta().cloneWithExternalUrls(imageUrlsUpdateFields.getExternalUrls());
final Image cloned = image.cloneWithMetaData(metaData.cloneWithCoreMeta(coreMeta));
storeImageTemplateMetaData(cloned);
}
public void updateImageTemplate(final ImageKey imageKey, final ImageTemplateRequest imageTemplateUpdateFields) {
final Image image = findImage(imageKey);
final Image cloned = templateMerger.mergeTemplateRequestIntoImage(image, imageTemplateUpdateFields);
LOGGER.info("{} merged with new template", cloned);
storeImageTemplateMetaData(cloned);
}
private synchronized Image storeImage(final Image image, final ImageStorage storageFunction) {
final InsertUpdateResult result = storageFunction.store(image);
if (result.isError()) {
LOGGER.error("Unable to store image {}. Update returned error: {}", image, result.getStatusMessage());
throw new RuntimeException("Failed to store image: " + result.getStatusMessage());
}
final Image storedImage = result.getResult();
updateCache(storedImage);
return storedImage;
}
private Image findImage(ImageKey imageKey) {
final Image image = repositoryCache.findImage(imageKey);
if (null == image) {
throw new IllegalArgumentException("Could not find image with key " + imageKey);
}
return image;
}
private void updateCache(final Image storedImage) {
final Repository imageParentRepository = repositoryCache.findItem(storedImage.getRepositoryKey());
if (null != imageParentRepository) {
imageParentRepository.addImage(storedImage);
} else {
LOGGER.warn("Could not find repository for image {}", storedImage);
}
}
@FunctionalInterface
interface ImageStorage {
InsertUpdateResult store(final Image image);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/service/ScheduleService.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.service;
import io.linuxserver.fleet.core.FleetAppController;
import io.linuxserver.fleet.v2.cache.BasicItemCache;
import io.linuxserver.fleet.v2.db.ScheduleDAO;
import io.linuxserver.fleet.v2.key.AbstractHasKey;
import io.linuxserver.fleet.v2.key.ScheduleKey;
import io.linuxserver.fleet.v2.thread.schedule.AppSchedule;
import io.linuxserver.fleet.v2.thread.schedule.ScheduleSpec;
import io.linuxserver.fleet.v2.thread.schedule.TimeWithUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Constructor;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.stream.Collectors;
public class ScheduleService extends AbstractAppService {
private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleService.class);
private final BasicItemCache scheduleCache;
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
private final ScheduleDAO scheduleDAO;
public ScheduleService(final FleetAppController controller, final ScheduleDAO scheduleDAO) {
super(controller);
this.scheduleCache = new BasicItemCache<>();
this.scheduleDAO = scheduleDAO;
}
public final void initialiseSchedules() {
final Set specs = scheduleDAO.fetchScheduleSpecs();
for (ScheduleSpec spec : specs) {
try {
final AppSchedule schedule = loadSchedule(spec);
LOGGER.info("Schedule loaded: {}", schedule);
loadOneSchedule(schedule);
} catch (Exception e) {
LOGGER.error("Unable to load schedule", e);
}
}
}
public final AppSchedule forceRun(final ScheduleKey scheduleKey) {
if (scheduleCache.isItemCached(scheduleKey)) {
final ScheduleWrapper wrapper = scheduleCache.findItem(scheduleKey);
LOGGER.info("Cancelling current run of schedule {}", wrapper.getName());
final boolean stopped = wrapper.getFuture().cancel(false);
if (stopped) {
LOGGER.info("Triggering re-run of schedule {}", wrapper.getName());
loadOneScheduleImmediately(wrapper.getSchedule());
return wrapper.getSchedule();
}
throw new RuntimeException("Schedule was already running and could not be stopped. Try again later.");
} else {
LOGGER.warn("Did not find cached schedule with key {}", scheduleKey);
throw new IllegalArgumentException("No schedule found with key " + scheduleKey);
}
}
public final List getLoadedSchedules() {
return scheduleCache.getAllItems().stream().map(ScheduleWrapper::getSchedule).collect(Collectors.toList());
}
private AppSchedule loadSchedule(final ScheduleSpec spec) {
try {
final Constructor extends AppSchedule> scheduleConstructor = spec.getScheduleClass().getDeclaredConstructor(ScheduleSpec.class,
FleetAppController.class);
return scheduleConstructor.newInstance(spec, getController());
} catch (Exception e) {
LOGGER.error("Unable to instantiate schedule for class {}", spec.getScheduleClass(), e);
throw new RuntimeException(e);
}
}
private void loadOneSchedule(final AppSchedule schedule) {
loadInternal(schedule, schedule.getDelay());
}
private void loadOneScheduleImmediately(final AppSchedule schedule) {
loadInternal(schedule, TimeWithUnit.Zero);
}
private void loadInternal(final AppSchedule schedule, final TimeWithUnit delay) {
final TimeWithUnit scheduleIntervalUnit = schedule.getInterval().convertToLowestUnit(delay);
final TimeWithUnit scheduleDelayUnit = delay.convertToLowestUnit(schedule.getInterval());
LOGGER.info("Scheduling with calculated interval/delay: {}/{}", scheduleIntervalUnit, scheduleDelayUnit);
final ScheduledFuture> future = executorService.scheduleAtFixedRate(schedule,
scheduleDelayUnit.getTimeDuration(),
scheduleIntervalUnit.getTimeDuration(),
scheduleIntervalUnit.getTimeUnit());
scheduleCache.addItem(new ScheduleWrapper(schedule, future));
}
public static class ScheduleWrapper extends AbstractHasKey {
private final ScheduledFuture> future;
private final AppSchedule schedule;
public ScheduleWrapper(final AppSchedule schedule, final ScheduledFuture> future) {
super(schedule.getKey());
this.future = future;
this.schedule = schedule;
}
public final ScheduledFuture> getFuture() {
return future;
}
public final AppSchedule getSchedule() {
return schedule;
}
public final String getName() {
return getSchedule().getName();
}
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/service/SynchronisationService.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.service;
import io.linuxserver.fleet.core.FleetAppController;
import io.linuxserver.fleet.v2.client.docker.queue.DockerApiDelegate;
import io.linuxserver.fleet.v2.client.docker.queue.DockerApiTaskConsumer;
import io.linuxserver.fleet.v2.client.docker.queue.DockerImageUpdateRequest;
import io.linuxserver.fleet.v2.client.docker.queue.TaskQueue;
import io.linuxserver.fleet.v2.key.ImageKey;
import io.linuxserver.fleet.v2.key.ImageLookupKey;
import io.linuxserver.fleet.v2.types.Image;
import io.linuxserver.fleet.v2.types.Repository;
import io.linuxserver.fleet.v2.types.docker.DockerImage;
import io.linuxserver.fleet.v2.types.internal.ImageOutlineRequest;
import java.util.List;
import java.util.stream.Collectors;
public class SynchronisationService extends AbstractAppService {
private final TaskQueue syncQueue;
private final DockerApiTaskConsumer taskConsumer;
public SynchronisationService(FleetAppController controller) {
super(controller);
syncQueue = new TaskQueue<>();
taskConsumer = new DockerApiTaskConsumer(this);
taskConsumer.start();
}
public final void synchroniseUpstreamRepository(final Repository repository) {
if (repository.isSyncEnabled()) {
getLogger().info("synchroniseUpstreamRepository checking {} for new images since last sync", repository);
final List apiImages = getController().getConfiguredDockerDelegate().getImagesForRepository(repository.getKey());
for (DockerImage apiImage : apiImages) {
final Image cachedImage = getController().getImageService()
.lookupImage(new ImageLookupKey(apiImage.getRepository() + "/" + apiImage.getName()));
if (null == cachedImage) {
getLogger().info("Found image from API which is not currently cached. Will add to system: {}", apiImage);
final ImageOutlineRequest outlineRequest = new ImageOutlineRequest(repository.getKey(),
apiImage.getName(),
apiImage.getDescription(),
apiImage.getBuildDate());
final Image imageOutline = getController().getImageService().createImageOutline(outlineRequest);
synchroniseImage(imageOutline.getKey());
}
}
} else {
getLogger().info("Will not check upstream repository {} as synchronisation is disabled", repository);
}
}
public final void synchroniseCachedRepository(final Repository repository) {
if (repository.isSyncEnabled()) {
for (Image image : repository.getImages()) {
if (image.isSyncEnabled()) {
boolean submitted = synchroniseImage(image.getKey());
if (!submitted) {
getLogger().warn("Unable to place sync request for image {} on queue", image.getKey());
}
} else {
getLogger().info("Ignoring sync request for {} as it has synchronisation disabled.", image);
}
}
} else {
getLogger().info("Will not synchronise images in {} as it has synchronisation disabled", repository);
}
}
public final boolean synchroniseImage(final ImageKey imageKey) {
return syncQueue.submitTask(new DockerImageUpdateRequest(imageKey));
}
public final TaskQueue getSyncQueue() {
return syncQueue;
}
public final DockerApiDelegate getConfiguredDockerDelegate() {
return getController().getConfiguredDockerDelegate();
}
public final boolean isConsumerRunning() {
return taskConsumer.isThreadRunning();
}
public final boolean isSyncQueueEmpty() {
return getSyncQueue().isEmpty();
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/service/UserService.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.service;
import io.linuxserver.fleet.auth.AuthenticationDelegate;
import io.linuxserver.fleet.auth.AuthenticationResult;
import io.linuxserver.fleet.auth.DefaultAuthenticationDelegate;
import io.linuxserver.fleet.auth.authenticator.DefaultUserAuthenticator;
import io.linuxserver.fleet.auth.security.PBKDF2PasswordEncoder;
import io.linuxserver.fleet.core.FleetAppController;
import io.linuxserver.fleet.db.query.InsertUpdateResult;
import io.linuxserver.fleet.v2.db.UserDAO;
import io.linuxserver.fleet.v2.key.UserKey;
import io.linuxserver.fleet.v2.types.User;
import io.linuxserver.fleet.v2.types.internal.UserOutlineRequest;
import java.util.List;
public class UserService extends AbstractAppService {
private final UserDAO userDAO;
private final AuthenticationDelegate authDelegate;
public UserService(final FleetAppController controller,
final UserDAO userDAO) {
super(controller);
this.userDAO = userDAO;
this.authDelegate = new DefaultAuthenticationDelegate(new DefaultUserAuthenticator(this,
new PBKDF2PasswordEncoder(getProperties().getAppSecret())));
createInitialAdminUser();
}
public final AuthenticationResult authenticateCredentials(final String username, final String password) {
return authDelegate.authenticate(username, password);
}
public final User lookUpUser(final String username) {
return userDAO.lookUpUser(username);
}
public final User fetchUser(final UserKey userKey) {
return userDAO.fetchUser(userKey);
}
public final List fetchAllUsers() {
return userDAO.fetchAllUsers();
}
public final void removeUser(final User user) {
userDAO.removeUser(user);
}
public final User updateUserPassword(final User user, final String password) {
final User updatedUser = user.cloneWithPassword(authDelegate.encodePassword(password));
final InsertUpdateResult result = userDAO.updateUser(updatedUser);
if (result.isError()) {
getLogger().error("Unable to update user: {}", result.getStatusMessage());
throw new RuntimeException("Unable to update user: " + result.getStatusMessage());
}
return result.getResult();
}
public final User createUserAndHashPassword(final UserOutlineRequest userOutlineRequest) {
return createUser(userOutlineRequest.cloneWithPassword(authDelegate.encodePassword(userOutlineRequest.getPassword())));
}
public final User createUser(final UserOutlineRequest userOutlineRequest) {
final InsertUpdateResult result = userDAO.createUser(userOutlineRequest);
if (result.isError()) {
getLogger().error("Unable to create new user: {}", result.getStatusMessage());
throw new RuntimeException("Unable to create new user: " + result.getStatusMessage());
}
return result.getResult();
}
private void createInitialAdminUser() {
if (fetchAllUsers().isEmpty()) {
getLogger().info("There are no users! Creating initial user with default credentials");
createUserAndHashPassword(UserOutlineRequest.InitialFirstLoadUser);
getLogger().warn("!!!!!!!!");
getLogger().warn("DEFAULT USER CREATED. CHANGE THE PASSWORD OR CREATE A NEW USER!");
getLogger().warn("!!!!!!!!");
}
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/service/util/TemplateMerger.java
================================================
/*
* Copyright (c) 2020 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.service.util;
import io.linuxserver.fleet.v2.types.Image;
import io.linuxserver.fleet.v2.types.docker.DockerCapability;
import io.linuxserver.fleet.v2.types.internal.ImageTemplateRequest;
import io.linuxserver.fleet.v2.types.meta.template.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TemplateMerger {
private static final Logger LOGGER = LoggerFactory.getLogger(TemplateMerger.class);
public final Image mergeTemplateRequestIntoImage(final Image image, final ImageTemplateRequest templateRequest) {
final ImageTemplateHolder templateHolder = makeTemplateHolder(templateRequest);
addMisc( templateRequest, templateHolder);
addPorts( templateRequest, templateHolder);
addVolumes( templateRequest, templateHolder);
addEnvironment(templateRequest, templateHolder);
addDevices( templateRequest, templateHolder);
return image.cloneWithMetaData(image.getMetaData().cloneWithTemplate(templateHolder));
}
private ImageTemplateHolder makeTemplateHolder(final ImageTemplateRequest templateRequest) {
return new ImageTemplateHolder(templateRequest.getRegistryUrl(),
templateRequest.getRestartPolicy(),
templateRequest.isHostNetworkEnabled(),
templateRequest.isPrivilegedMode());
}
private void addPorts(final ImageTemplateRequest request, final ImageTemplateHolder holder) {
for (ImageTemplateRequest.TemplateItem port : request.getPorts()) {
holder.addPort(new PortTemplateItem(Integer.parseInt(port.getName()),
port.getDescription(),
PortTemplateItem.Protocol.fromName(port.getSecondaryField())));
}
}
private void addVolumes(final ImageTemplateRequest request, final ImageTemplateHolder holder) {
for (ImageTemplateRequest.TemplateItem volume : request.getVolumes()) {
holder.addVolume(new VolumeTemplateItem(volume.getName(),
volume.getDescription(),
volume.getSecondaryField()));
}
}
private void addEnvironment(final ImageTemplateRequest request, final ImageTemplateHolder holder) {
for (ImageTemplateRequest.TemplateItem env : request.getEnvironment()) {
holder.addEnvironment(new EnvironmentTemplateItem(env.getName(), env.getDescription(), env.getSecondaryField()));
}
}
private void addDevices(final ImageTemplateRequest request, final ImageTemplateHolder holder) {
for (ImageTemplateRequest.TemplateItem device : request.getDevices()) {
holder.addDevice(new DeviceTemplateItem(device.getName(), device.getDescription()));
}
}
private void addMisc(final ImageTemplateRequest request, final ImageTemplateHolder holder) {
if (null != request.getCapabilities()) {
for (String capability : request.getCapabilities()) {
try {
holder.addCapability(DockerCapability.valueOf(capability));
} catch (IllegalArgumentException e) {
LOGGER.warn("Attempted to add unknown capability {}", capability);
}
}
}
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/AbstractAppTask.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread;
import io.linuxserver.fleet.v2.LoggerOwner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractAppTask implements AsyncTask, LoggerOwner {
private final Logger LOGGER = LoggerFactory.getLogger(getClass());
public final String name;
public AbstractAppTask(final String name) {
if (null == name) {
throw new IllegalArgumentException("name must not be null");
}
this.name = name;
}
@Override
public RESPONSE performTaskOn(final DELEGATE delegate) {
try {
return performTaskInternal(delegate);
} catch (Exception e) {
LOGGER.error("Unable to complete task", e);
throw new TaskExecutionException(e);
}
}
@Override
public final Logger getLogger() {
return LOGGER;
}
protected abstract RESPONSE performTaskInternal(final DELEGATE delegate);
@Override
public String toString() {
return "AsyncTask[" + name + "]";
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof AbstractAppTask)) {
return false;
}
return ((AbstractAppTask, ?>) o).name.equals(name);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/AbstractAppThread.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread;
import io.linuxserver.fleet.core.FleetAppController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractAppThread extends Thread {
private final Logger LOGGER = LoggerFactory.getLogger(getClass());
private ThreadStatus status = ThreadStatus.Stopped;
private final FleetAppController controller;
public AbstractAppThread(final FleetAppController controller, final String name) {
this.controller = controller;
setName(name);
}
public final FleetAppController getController() {
return controller;
}
@Override
public synchronized void run() {
try {
LOGGER.info("Starting thread...");
status = ThreadStatus.Running;
while (isThreadRunning()) {
doRunSinglePass();
}
} catch (Exception e) {
LOGGER.error("Thread has encountered an exception it cannot handle", e);
controller.handleException(e);
LOGGER.info("Stopping thread...");
status = ThreadStatus.Stopped;
}
}
public final boolean isThreadRunning() {
return status == ThreadStatus.Running;
}
protected final Logger getLogger() {
return LOGGER;
}
protected abstract void doRunSinglePass() throws Exception;
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/AbstractTaskQueueConsumer.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread;
import io.linuxserver.fleet.core.FleetAppController;
import io.linuxserver.fleet.v2.client.docker.queue.TaskQueue;
public abstract class AbstractTaskQueueConsumer>
extends AbstractAppThread {
private final TaskQueue taskQueue;
private final DELEGATE taskDelegate;
public AbstractTaskQueueConsumer(final FleetAppController controller,
final DELEGATE delegate,
final TaskQueue queue,
final String consumerThreadName) {
super(controller, consumerThreadName);
taskQueue = queue;
taskDelegate = delegate;
}
@Override
protected void doRunSinglePass() throws Exception {
final T task = taskQueue.retrieveNextTask();
try {
getLogger().info("Processing single task {}", task);
final R response = task.performTaskOn(taskDelegate);
handleTaskResponse(response);
} catch (TaskExecutionException e) {
getLogger().error("Unable to complete the processing of task {}", task, e);
}
}
protected abstract void handleTaskResponse(final R response);
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/AsyncTask.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread;
public interface AsyncTask {
RESPONSE performTaskOn(DELEGATE delegate);
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/AsyncTaskDelegate.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread;
import io.linuxserver.fleet.core.FleetAppController;
public interface AsyncTaskDelegate {
FleetAppController getController();
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/AsyncTaskResponse.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread;
public interface AsyncTaskResponse {
void handleResponse();
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/TaskExecutionException.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread;
public class TaskExecutionException extends RuntimeException {
public TaskExecutionException(final Exception cause) {
super(cause);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/TaskResponseControllerProxy.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread;
import io.linuxserver.fleet.core.FleetAppController;
public class TaskResponseControllerProxy implements AsyncTaskResponse {
private final FleetAppController controller;
private final R response;
public TaskResponseControllerProxy(final FleetAppController controller, final R response) {
this.controller = controller;
this.response = response;
}
@Override
public void handleResponse() {
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/ThreadStatus.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread;
public enum ThreadStatus {
Stopped, Running;
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/schedule/AbstractAppSchedule.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread.schedule;
import io.linuxserver.fleet.core.FleetAppController;
import io.linuxserver.fleet.v2.LoggerOwner;
import io.linuxserver.fleet.v2.key.AbstractHasKey;
import io.linuxserver.fleet.v2.key.ScheduleKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.atomic.AtomicReference;
public abstract class AbstractAppSchedule extends AbstractHasKey implements AppSchedule, LoggerOwner {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final FleetAppController controller;
private final AtomicReference spec;
private final LocalDateTime intantiatedAt;
private final AtomicReference lastRun;
private final AtomicReference lastRunDuration;
public AbstractAppSchedule(final ScheduleSpec spec,
final FleetAppController controller) {
super(spec.getKey());
this.intantiatedAt = LocalDateTime.now();
this.controller = controller;
this.spec = new AtomicReference<>(spec);
this.lastRun = new AtomicReference<>();
this.lastRunDuration = new AtomicReference<>(Duration.ZERO);
}
protected final FleetAppController getController() {
return controller;
}
public final ScheduleSpec getSpec() {
return spec.get();
}
@Override
public final String getName() {
return getSpec().getScheduleName();
}
@Override
public final LocalDateTime getLastRunTime() {
return lastRun.get();
}
@Override
public final LocalDateTime getNextRunTime() {
final ScheduleSpec scheduleSpec = getSpec();
final TimeWithUnit interval = scheduleSpec.getInterval();
if (null == getLastRunTime()) {
if (getSpec().getDelayOffset().isGreaterThanZero()) {
return intantiatedAt.plus(scheduleSpec.getDelayOffset().getTimeDuration(), scheduleSpec.getDelayOffset().getChronoUnit());
} else {
return intantiatedAt.plus(interval.getTimeDuration(), interval.getChronoUnit());
}
}
return getLastRunTime().plus(interval.getTimeDuration(), interval.getChronoUnit());
}
@Override
public final Duration getLastRunDuration() {
return lastRunDuration.get();
}
@Override
public final TimeWithUnit getInterval() {
return getSpec().getInterval();
}
@Override
public TimeWithUnit getDelay() {
return getSpec().getDelayOffset();
}
@Override
public final Logger getLogger() {
return logger;
}
@Override
public void run() {
final LocalDateTime startTime = LocalDateTime.now();
final String scheduleName = getSpec().getScheduleName();
try {
if (isAllowedToExecute()) {
logger.info("Starting run of schedule {}", scheduleName);
executeSchedule();
logger.info("Run of schedule {} finished.", scheduleName);
} else {
logger.info("Schedule is currently not allowed to run. Will log run but will skip until next time.");
}
} catch (Exception e) {
logger.error("Caught unhandled exception during running of schedule {}", scheduleName, e);
}
final LocalDateTime endTime = LocalDateTime.now();
lastRun.set(endTime);
lastRunDuration.set(Duration.between(startTime, endTime));
}
@Override
public String toString() {
return "Name=" + getName() + ", Interval=" + getInterval() + ", InitialDelay=" + getDelay();
}
protected boolean isAllowedToExecute() {
return true;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/schedule/AppSchedule.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread.schedule;
import io.linuxserver.fleet.v2.key.HasKey;
import io.linuxserver.fleet.v2.key.ScheduleKey;
import java.time.Duration;
import java.time.LocalDateTime;
public interface AppSchedule extends HasKey, Runnable {
String getName();
LocalDateTime getLastRunTime();
LocalDateTime getNextRunTime();
Duration getLastRunDuration();
TimeWithUnit getDelay();
TimeWithUnit getInterval();
void executeSchedule();
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/schedule/CheckAppVersionSchedule.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread.schedule;
import io.linuxserver.fleet.core.FleetAppController;
public class CheckAppVersionSchedule extends AbstractAppSchedule {
public CheckAppVersionSchedule(final ScheduleSpec spec,
final FleetAppController controller) {
super(spec, controller);
}
@Override
public void executeSchedule() {
getLogger().info("Currently not implemented. This is a placeholder schedule");
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/schedule/ScheduleSpec.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread.schedule;
import io.linuxserver.fleet.v2.key.AbstractHasKey;
import io.linuxserver.fleet.v2.key.ScheduleKey;
public final class ScheduleSpec extends AbstractHasKey {
private final String scheduleName;
private final TimeWithUnit interval;
private final TimeWithUnit delayOffset;
private final Class extends AppSchedule> specForClass;
private ScheduleSpec(final ScheduleKey key,
final String scheduleName,
final TimeWithUnit interval,
final TimeWithUnit delayOffset,
final Class extends AppSchedule> specForClass) {
super(key);
this.scheduleName = scheduleName;
this.interval = interval;
this.delayOffset = delayOffset;
this.specForClass = specForClass;
}
public static ScheduleSpec makeInitial(final ScheduleKey key,
final String scheduleName,
final TimeWithUnit interval,
final TimeWithUnit delayOffset,
final Class extends AppSchedule> specForClass) {
return new ScheduleSpec(key,
scheduleName,
interval,
delayOffset,
specForClass);
}
public final String getScheduleName() {
return scheduleName;
}
public final TimeWithUnit getInterval() {
return interval;
}
public final TimeWithUnit getDelayOffset() {
return delayOffset;
}
public final Class extends AppSchedule> getScheduleClass() {
return specForClass;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/schedule/TidyHistoricDataSchedule.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread.schedule;
import io.linuxserver.fleet.core.FleetAppController;
public class TidyHistoricDataSchedule extends AbstractAppSchedule {
public TidyHistoricDataSchedule(final ScheduleSpec spec,
final FleetAppController controller) {
super(spec, controller);
}
@Override
public void executeSchedule() {
getLogger().info("Currently not implemented. This is a placeholder schedule");
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/schedule/TimeWithUnit.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread.schedule;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
public class TimeWithUnit {
public static final TimeWithUnit Zero = new TimeWithUnit(0, TimeUnit.SECONDS);
private final long timeDuration;
private final TimeUnit timeUnit;
public TimeWithUnit(final long timeDuration, final TimeUnit timeUnit) {
this.timeDuration = timeDuration;
this.timeUnit = timeUnit;
}
public static TimeWithUnit valueOf(final String value) {
if (value.matches("\\d+:(seconds|minutes|hours|days)")) {
final String[] values = value.split(":");
return new TimeWithUnit(Integer.parseInt(values[0]), TimeUnit.valueOf(values[1].toUpperCase()));
}
throw new IllegalArgumentException("Invalid TimeWithUnit value " + value);
}
public final TimeWithUnit convertToLowestUnit(final TimeWithUnit otherUnit) {
if (getTimeUnit().compareTo(otherUnit.getTimeUnit()) < 0) {
return this;
}
return new TimeWithUnit(otherUnit.getTimeUnit().convert(getTimeDuration(), getTimeUnit()),
otherUnit.getTimeUnit());
}
public final long getTimeDuration() {
return timeDuration;
}
public final ChronoUnit getChronoUnit() {
return timeUnit.toChronoUnit();
}
public final TimeUnit getTimeUnit() {
return timeUnit;
}
public final boolean isGreaterThanZero() {
return getTimeDuration() > 0;
}
@Override
public final String toString() {
return getTimeDuration() + ":" + getTimeUnit().name().toLowerCase();
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/schedule/cache/RefreshCacheSchedule.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread.schedule.cache;
import io.linuxserver.fleet.core.FleetAppController;
import io.linuxserver.fleet.v2.thread.schedule.AbstractAppSchedule;
import io.linuxserver.fleet.v2.thread.schedule.ScheduleSpec;
public final class RefreshCacheSchedule extends AbstractAppSchedule {
public RefreshCacheSchedule(final ScheduleSpec spec,
final FleetAppController controller) {
super(spec, controller);
}
@Override
public void executeSchedule() {
getController().getImageService().reloadCache();
}
@Override
protected boolean isAllowedToExecute() {
return getController().getSynchronisationService().isSyncQueueEmpty();
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/schedule/sync/AllImagesSyncSchedule.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread.schedule.sync;
import io.linuxserver.fleet.core.FleetAppController;
import io.linuxserver.fleet.v2.thread.schedule.AbstractAppSchedule;
import io.linuxserver.fleet.v2.thread.schedule.ScheduleSpec;
import io.linuxserver.fleet.v2.types.Repository;
import java.util.List;
public final class AllImagesSyncSchedule extends AbstractAppSchedule {
public AllImagesSyncSchedule(final ScheduleSpec spec,
final FleetAppController controller) {
super(spec, controller);
}
@Override
public void executeSchedule() {
final List allRepositories = getController().getImageService().getAllRepositories();
for (Repository repository : allRepositories) {
getController().getSynchronisationService().synchroniseCachedRepository(repository);
}
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/schedule/sync/CleanRemovedImagesSchedule.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread.schedule.sync;
import io.linuxserver.fleet.core.FleetAppController;
import io.linuxserver.fleet.v2.thread.schedule.AbstractAppSchedule;
import io.linuxserver.fleet.v2.thread.schedule.ScheduleSpec;
import io.linuxserver.fleet.v2.types.Image;
import io.linuxserver.fleet.v2.types.Repository;
import io.linuxserver.fleet.v2.types.docker.DockerImage;
import java.util.List;
import java.util.stream.Collectors;
public final class CleanRemovedImagesSchedule extends AbstractAppSchedule {
public CleanRemovedImagesSchedule(final ScheduleSpec spec,
final FleetAppController controller) {
super(spec, controller);
}
@Override
public void executeSchedule() {
final List allRepositories = getController().getImageService().getAllRepositories();
for (Repository repository : allRepositories) {
getLogger().info("Checking for removed upstream images in " + repository);
final List apiImages = getController().getConfiguredDockerDelegate().getImagesForRepository(repository.getKey());
if (apiImages.isEmpty()) {
getLogger().warn("executeSchedule found no images for repository " + repository + " upstream. Playing it safe and ignoring clean function.");
} else {
final List imageNames = apiImages.stream().map(DockerImage::getName).collect(Collectors.toList());
for (Image cachedImage : repository.getImages()) {
if (!imageNames.contains(cachedImage.getName())) {
getLogger().info("Found removed image upstream. Deleting from cache: " + cachedImage);
getController().getImageService().removeImage(cachedImage.getKey());
}
}
}
}
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/thread/schedule/sync/GetMissingImagesSchedule.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.thread.schedule.sync;
import io.linuxserver.fleet.core.FleetAppController;
import io.linuxserver.fleet.v2.thread.schedule.AbstractAppSchedule;
import io.linuxserver.fleet.v2.thread.schedule.ScheduleSpec;
import io.linuxserver.fleet.v2.types.Repository;
import java.util.List;
public final class GetMissingImagesSchedule extends AbstractAppSchedule {
public GetMissingImagesSchedule(final ScheduleSpec spec,
final FleetAppController controller) {
super(spec, controller);
}
@Override
public void executeSchedule() {
final List cachedRepositories = getController().getImageService().getAllRepositories();
for (Repository repository : cachedRepositories) {
getController().getSynchronisationService().synchroniseUpstreamRepository(repository);
}
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/types/AbstractSyncItem.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.types;
import io.linuxserver.fleet.v2.key.AbstractHasKey;
import io.linuxserver.fleet.v2.key.Key;
import io.linuxserver.fleet.v2.types.meta.ItemSyncSpec;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public abstract class AbstractSyncItem> extends AbstractHasKey implements HasSyncSpec {
private final ItemSyncSpec syncSpec;
public AbstractSyncItem(final KEY key, final ItemSyncSpec syncSpec) {
super(key);
this.syncSpec = syncSpec;
}
public abstract ITEM cloneWithSyncSpec(final ItemSyncSpec syncSpec);
public final ItemSyncSpec getSpec() {
return syncSpec;
}
@Override
public boolean isSyncEnabled() {
return getSpec().isSynchronised();
}
@Override
public boolean isStable() {
return getSpec().isStable();
}
@Override
public boolean isDeprecated() {
return getSpec().isDeprecated();
}
@Override
public String getVersionMask() {
return getSpec().getVersionMask();
}
@Override
public boolean isHidden() {
return getSpec().isHidden();
}
public final String getMaskedVersion(final Tag tag) {
if (null == tag) {
return null;
}
return extractMaskedVersion(tag.getVersion());
}
private String extractMaskedVersion(final String tagVersion) {
final String versionMask = getVersionMask();
if (null != versionMask) {
final Pattern pattern = Pattern.compile(versionMask);
final Matcher matcher = pattern.matcher(tagVersion);
if (matcher.matches()) {
final StringBuilder tagBuilder = new StringBuilder();
for (int groupNum = 1; groupNum <= matcher.groupCount(); groupNum++)
tagBuilder.append(matcher.group(groupNum));
return tagBuilder.toString();
}
}
return tagVersion;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/types/AppAlert.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.types;
import io.linuxserver.fleet.v2.key.AbstractHasKey;
import io.linuxserver.fleet.v2.key.AlertKey;
import java.time.LocalDateTime;
import java.util.UUID;
public class AppAlert extends AbstractHasKey {
private final AlertLevel alertLevel;
private final LocalDateTime alertDate;
private final String subject;
private final String alertMessage;
private AppAlert(final AlertKey key,
final AlertLevel level,
final LocalDateTime alertDate,
final String subject,
final String alertMessage) {
super(key);
this.alertLevel = level;
this.alertDate = LocalDateTime.of(alertDate.toLocalDate(), alertDate.toLocalTime());
this.subject = subject;
this.alertMessage = alertMessage;
}
public static AppAlert makeAlert(final AlertLevel alertLevel, final String subject, final String alertMessage) {
return new AppAlert(new AlertKey(UUID.randomUUID().toString()),
alertLevel,
LocalDateTime.now(),
subject,
alertMessage);
}
public final LocalDateTime getAlertDate() {
return LocalDateTime.of(alertDate.toLocalDate(), alertDate.toLocalTime());
}
public final String getSubject() {
return subject;
}
public final String getAlertMessage() {
return alertMessage;
}
public final AlertLevel getAlertLevel() {
return alertLevel;
}
public final boolean isSystemAlert() {
return getAlertLevel().isSystem();
}
public enum AlertLevel {
Info, Warning, Error, System;
public final boolean isInfo() {
return this == Info;
}
public final boolean isSystem() {
return this == System;
}
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/types/FilePathDetails.java
================================================
/*
* Copyright (c) 2020 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.types;
public class FilePathDetails {
private final String fileNameWithExtension;
private final String fullAbsolutePath;
private final String publicSafePath;
public FilePathDetails(final String fileNameWithExtension,
final String fullAbsolutePath,
final String publicSafePath) {
this.fileNameWithExtension = fileNameWithExtension;
this.fullAbsolutePath = fullAbsolutePath;
this.publicSafePath = publicSafePath;
}
public final String getFileNameWithExtension() {
return fileNameWithExtension;
}
public final String getFullAbsolutePath() {
return fullAbsolutePath;
}
public final String getPublicSafePathWithFileName() {
return publicSafePath + "/" + getFileNameWithExtension();
}
public final String getFullAbsolutePathWithFileName() {
return getFullAbsolutePath() + "/" + getFileNameWithExtension();
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/types/HasSyncSpec.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.types;
public interface HasSyncSpec {
boolean isSyncEnabled();
boolean isStable();
boolean isDeprecated();
String getVersionMask();
boolean isHidden();
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/types/Image.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.types;
import io.linuxserver.fleet.v2.key.HasKey;
import io.linuxserver.fleet.v2.key.ImageKey;
import io.linuxserver.fleet.v2.key.RepositoryKey;
import io.linuxserver.fleet.v2.types.meta.ImageMetaData;
import io.linuxserver.fleet.v2.types.meta.ItemSyncSpec;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
public class Image extends AbstractSyncItem {
private static final DateTimeFormatter DefaultImageTimeFormat = DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm:ss");
private final ImageCountData countData;
private final String description;
private final LocalDateTime lastUpdated;
private final Set tagBranches;
private final ImageMetaData metaData;
public Image(final ImageKey key,
final ItemSyncSpec syncSpec,
final ImageMetaData metaData,
final ImageCountData countData,
final String description,
final LocalDateTime lastUpdated) {
super(key, syncSpec);
this.countData = countData;
this.metaData = metaData;
this.description = description;
this.lastUpdated = parseDateTime(lastUpdated);
this.tagBranches = new HashSet<>();
}
public final Image cloneForUpdate(final long pullCount,
final int starCount,
final String description,
final LocalDateTime lastUpdated) {
final Image cloned = new Image(getKey(), getSpec(), getMetaData(), new ImageCountData(pullCount, starCount), description, lastUpdated);
tagBranches.forEach(t -> cloned.addTagBranch(t.cloneForUpdate()));
return cloned;
}
public final Image cloneForUpdate() {
return cloneWithSyncSpec(getSpec());
}
public final Image cloneWithMetaData(final ImageMetaData metaData) {
final Image cloned = new Image(getKey(), getSpec(), metaData, new ImageCountData(getPullCount(), getStarCount()), getDescription(), getLastUpdated());
tagBranches.forEach(t -> cloned.addTagBranch(t.cloneForUpdate()));
return cloned;
}
@Override
public final Image cloneWithSyncSpec(final ItemSyncSpec syncSpec) {
final Image cloned = new Image(getKey(), syncSpec, getMetaData(), countData, getDescription(), getLastUpdated());
tagBranches.forEach(t -> cloned.addTagBranch(t.cloneForUpdate()));
return cloned;
}
public final String getFullName() {
return getRepositoryName() + "/" + getName();
}
public final RepositoryKey getRepositoryKey() {
return getKey().getRepositoryKey();
}
public final String getRepositoryName() {
return getRepositoryKey().getName();
}
public final String getName() {
return getKey().getName();
}
public final String getDescription() {
return description;
}
public final ImageMetaData getMetaData() {
return metaData;
}
public final LocalDateTime getLastUpdated() {
return parseDateTime(lastUpdated);
}
public final String getLastUpdatedAsString() {
final LocalDateTime lastUpdated = parseDateTime(this.lastUpdated);
return null == lastUpdated ? null : DefaultImageTimeFormat.format(lastUpdated);
}
public final List getTagBranches() {
return new ArrayList<>(tagBranches);
}
public final TagBranch findTagBranchByName(final String branchName) {
for (TagBranch tagBranch : tagBranches) {
if (tagBranch.getBranchName().equals(branchName)) {
return tagBranch;
}
}
return null;
}
public final void addTagBranch(final TagBranch tagBranch) {
final boolean added = tagBranches.add(tagBranch);
if (!added) {
throw new IllegalArgumentException("TagBranch " + tagBranch + " already present in Image");
}
}
public final void removeTagBranch(final TagBranch tagBranch) {
final Iterator iterator = tagBranches.iterator();
while (iterator.hasNext()) {
final TagBranch current = iterator.next();
if (current.equals(tagBranch) && !current.isBranchProtected()) {
iterator.remove();
}
}
}
public final long getPullCount() {
return countData.getPullCount();
}
public final int getStarCount() {
return countData.getStarCount();
}
public final Tag getLatestTag() {
for (TagBranch storedTagBranch : getTagBranches()) {
if (storedTagBranch.isNamedLatest()) {
return storedTagBranch.getLatestTag();
}
}
return Tag.DefaultUnknown;
}
@Override
public final int compareTo(final HasKey o) {
return getKey().getName().compareTo(o.getKey().getName());
}
@Override
public String toString() {
return getRepositoryName() + "/" + getName() + "@" + getLatestTag();
}
private LocalDateTime parseDateTime(final LocalDateTime dateTime) {
return null == dateTime ? null : LocalDateTime.of(dateTime.toLocalDate(), dateTime.toLocalTime());
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/types/ImageCountData.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.types;
public class ImageCountData {
private final long pullCount;
private final int starCount;
public ImageCountData(final long pullCount, final int starCount) {
this.pullCount = pullCount;
this.starCount = starCount;
}
public final long getPullCount() {
return pullCount;
}
public final int getStarCount() {
return starCount;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/types/Repository.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.types;
import io.linuxserver.fleet.v2.cache.ImageCache;
import io.linuxserver.fleet.v2.key.RepositoryKey;
import io.linuxserver.fleet.v2.types.meta.ItemSyncSpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Repository extends AbstractSyncItem {
private final ImageCache images;
public Repository(final RepositoryKey key, final ItemSyncSpec syncSpec) {
super(key, syncSpec);
images = new ImageCache();
}
@Override
public Repository cloneWithSyncSpec(final ItemSyncSpec syncSpec) {
final Repository cloned = new Repository(getKey(), syncSpec);
images.getAllItems().forEach(i -> addImage(i.cloneWithSyncSpec(i.getSpec())));
return cloned;
}
public final void addImage(final Image image) {
images.addItem(image);
}
public final String getName() {
return getKey().getName();
}
public final List getImages() {
final List imageList = new ArrayList<>(images.getAllItems());
Collections.sort(imageList);
return imageList;
}
public final long getTotalPulls() {
long totalPulls = 0;
for (Image image : getImages()) {
totalPulls += image.getPullCount();
}
return totalPulls;
}
public final int getTotalStars() {
int totalStars = 0;
for (Image image : getImages()) {
totalStars += image.getStarCount();
}
return totalStars;
}
@Override
public final boolean isHidden() {
return !isSyncEnabled();
}
@Override
public final boolean isStable() {
return true;
}
@Override
public final boolean isDeprecated() {
return false;
}
public final void removeImage(final Image image) {
images.removeItem(image.getKey());
}
@Override
public final String toString() {
return getName() + "[nImages=" + images.size() + "]";
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/types/Tag.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.types;
import java.time.LocalDateTime;
import java.util.*;
public class Tag {
public static final Tag DefaultUnknown = new Tag("Unknown", null, Collections.emptySet());
private final String version;
private final Set digests;
private final LocalDateTime buildDate;
public Tag(final String version, final LocalDateTime buildDate, final Set digests) {
this.version = version;
this.digests = Collections.unmodifiableSet(digests);
this.buildDate = (null == buildDate ? null : LocalDateTime.of(buildDate.toLocalDate(), buildDate.toLocalTime()));
}
public final List getDigests() {
return new ArrayList<>(digests);
}
public String getVersion() {
return version;
}
public LocalDateTime getBuildDate() {
if (null != buildDate) {
return LocalDateTime.of(buildDate.toLocalDate(), buildDate.toLocalTime());
}
return null;
}
@Override
public String toString() {
return version;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/types/TagBranch.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.types;
import io.linuxserver.fleet.v2.key.AbstractHasKey;
import io.linuxserver.fleet.v2.key.TagBranchKey;
import java.util.concurrent.atomic.AtomicReference;
public class TagBranch extends AbstractHasKey {
private final String branchName;
private final AtomicReference branchProtected;
private final AtomicReference latestTag;
public TagBranch(final TagBranchKey tagBranchKey, final String tagBranchName, final boolean branchProtected, final Tag latestTag) {
super(tagBranchKey);
this.branchName = tagBranchName;
this.branchProtected = new AtomicReference<>(branchProtected);
this.latestTag = new AtomicReference<>(latestTag);
}
public final void updateLatestTag(final Tag latestTag) {
this.latestTag.set(latestTag);
}
public final void setBranchProtected(final boolean branchProtected) {
this.branchProtected.set(branchProtected);
}
public final String getBranchName() {
return branchName;
}
public final Tag getLatestTag() {
return latestTag.get();
}
public final boolean isNamedLatest() {
return "latest".equals(getBranchName());
}
public final boolean isBranchProtected() {
return branchProtected.get();
}
public final TagBranch cloneForUpdate() {
return new TagBranch(getKey(), getBranchName(), isBranchProtected(), getLatestTag());
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/types/TagDigest.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.types;
import java.util.Objects;
public class TagDigest implements Comparable {
private final long size;
private final String digest;
private final String architecture;
private final String archVariant;
public TagDigest(final long size, final String digest, final String architecture, final String archVariant) {
this.size = size;
this.digest = digest;
this.architecture = architecture;
this.archVariant = archVariant;
}
public final long getSize() {
return size;
}
public final String getDigest() {
return digest;
}
public final String getArchitecture() {
return architecture;
}
public final String getArchVariant() {
return archVariant;
}
@Override
public int hashCode() {
return Objects.hash(digest, architecture, archVariant);
}
@Override
public boolean equals(Object obj) {
if (null == obj) {
return false;
}
if (!(obj instanceof TagDigest)) {
return false;
}
final TagDigest other = (TagDigest) obj;
return Objects.equals(digest, other.digest) &&
Objects.equals(architecture, other.architecture) &&
Objects.equals(archVariant, other.archVariant);
}
@Override
public String toString() {
return "DIGEST:" + digest + "--" + architecture + "/" + archVariant;
}
@Override
public int compareTo(TagDigest o) {
if (null == o) {
return 1;
}
return toString().compareTo(o.toString());
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/types/User.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.types;
import io.linuxserver.fleet.v2.key.AbstractHasKey;
import io.linuxserver.fleet.v2.key.UserKey;
import io.linuxserver.fleet.v2.web.AppRole;
import java.time.LocalDateTime;
public class User extends AbstractHasKey {
private final String username;
private final String password;
private final AppRole role;
private final LocalDateTime modifiedTime;
public User(final UserKey key,
final String username,
final String password,
final LocalDateTime modifiedTime,
final AppRole role) {
super(key);
this.username = username;
this.password = password;
this.modifiedTime = LocalDateTime.of(modifiedTime.toLocalDate(), modifiedTime.toLocalTime());
this.role = role;
}
public final String getUsername() {
return username;
}
public final String getPassword() {
return password;
}
public final AppRole getRole() {
return role;
}
public final LocalDateTime getModifiedTime() {
if (null != modifiedTime) {
return LocalDateTime.of(modifiedTime.toLocalDate(), modifiedTime.toLocalTime());
}
return null;
}
public final User cloneWithPassword(final String hashedPassword) {
return new User(getKey(), username, hashedPassword, LocalDateTime.now(), role);
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/types/api/AbstractApiWrapper.java
================================================
/*
* Copyright (c) 2019 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.types.api;
public class AbstractApiWrapper {
private final T originalObject;
public AbstractApiWrapper(final T originalObject) {
this.originalObject = originalObject;
}
protected final T getOriginalObject() {
return originalObject;
}
}
================================================
FILE: src/main/java/io/linuxserver/fleet/v2/types/api/ApiImagePullHistoryWrapper.java
================================================
/*
* Copyright (c) 2020 LinuxServer.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package io.linuxserver.fleet.v2.types.api;
import io.linuxserver.fleet.v2.types.meta.history.ImagePullStatistic;
import java.util.*;
import java.util.stream.Collectors;
public class ApiImagePullHistoryWrapper extends AbstractApiWrapper> {
private final ImagePullStatistic.StatGroupMode groupMode;
public ApiImagePullHistoryWrapper(final List originalObject,
final ImagePullStatistic.StatGroupMode groupMode) {
super(originalObject);
this.groupMode = groupMode;
}
public final String getGroupModeDataPoint() {
return groupMode.getDataPoint();
}
public final List getLabels() {
return getOriginalObject().stream().map(ImagePullStatistic::getGroupedDateTime).collect(Collectors.toList());
}
public final List getPulls() {
return getOriginalObject().stream().map(ImagePullStatistic::getPullCount).collect(Collectors.toList());
}
public final long getMean() {
return (long) getPullDifferential().getPulls().stream().mapToLong(Long::longValue).average().orElse(0.0);
}
public final PullDifferentialsWithLabels getPullDifferential() {
final PullDifferentialsWithLabels differentialsWithLabels = new PullDifferentialsWithLabels();
int i;
for (i = 1; i < (getOriginalObject().size() - 1); i++) {
final ImagePullStatistic previousStat = getOriginalObject().get(i - 1);
final ImagePullStatistic currentStat = getOriginalObject().get(i);
differentialsWithLabels.addDifferential(currentStat.getGroupedDateTime(), (currentStat.getPullCount() - previousStat.getPullCount()));
}
return differentialsWithLabels;
}
public static class PullDifferentialsWithLabels {
private final Map