Conectar a un Docker Registry inseguro en Ubuntu con Docker v17.06+

Al actualizar los paquetes de mi instalación de Ubuntu, he visto que se ha actualizado docker.

A partir de ese momento no he podido hacer push/pull al registry que tenemos (está instalado de forma insegura en un servidor de la LAN).

En el access.log del registry veo las siguientes entradas de error 404:

84.88.79.211 - - [05/Jul/2017:07:55:17 +0000] "POST /v2/myimagename/blobs/uploads/ HTTP/1.1" 404 233 "-" "docker/17.06.0-ce go/go1.8.3 git-commit/02c1d87 kernel/4.4.0-83-generic os/linux arch/amd64 UpstreamClient(Docker-Client/17.06.0-ce \(linux\))"

Por defecto Docker, al hacer las peticiones contra el registry, trata de hacerlas contra los servicios “v2” y en caso de no encontrarlos intenta contra “v1”. En mi caso esto último no venía sucediendo.

En las release notes de la versión 16.03 veo lo siguiente:

Note: Docker 17.06 by default disables communication with legacy (v1) registries. If you require interaction with registries that have not yet migrated to the v2 protocol, set the --disable-legacy-registry=false daemon option. Interaction with v1 registries will be removed in Docker 17.12.

La solución pasa por actualizar el docker registry ya que en la versión 17.12 esta funcionalidad directamente no existirá. Mientras tanto, podemos añadir las siguientes líneas al fichero /etc/docker/daemon.json

{
  "disable-legacy-registry": false,
  "insecure-registries" : ["myregistryhost:port"]
}

Saludos

Angular Proxy Config para evitar errores de Cross Domain

En mi entorno de desarrollo, tengo un webservice hecho con Java y que està accesible por el puerto 8888.

El frontend es una aplicación javascript Angular y que está accesible por el puerto 4200.

Para evitar problemas de Cross domain (el browser impide que el frontend ejecute peticiones HTTP contra un host/puerto distinto).

Crear el fichero proxy.conf.json en la raíz del proyecto Angular:

{
    "/oauth": {
        "target": "http://localhost:8888",
        "secure": false
    } 
}

Arrancar el servidor de desarrollo indicando la configuración proxy.

ng serve --proxy-config proxy.conf.json

De esta forma, cuando se haga una petición a http://localhost:4200/oauth, realmente se estará ejecutando contra http://localhost:8888/oauth.

En producción tendremos que configurar NGINX de la misma manera.

Ocultar método soportado por endpoint de terceros. Spring y Swagger.

A veces, queremos ocultar que uno de los métodos soportados por un endpoint que no controlamos no aparezca en Swagger.

No he visto que exista ninguna función para hacer esto.

Este es el Predicate que he implementado.

import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.google.common.base.Predicate;

import springfox.documentation.RequestHandler;

public class IsRequestMethodPredicate implements Predicate<RequestHandler> {

	private final String path;
	private final RequestMethod method;

	public IsRequestMethodPredicate(String path, RequestMethod method) {
		this.path = path;
		this.method = method;
	}

	public static IsRequestMethodPredicate isRequestMethod(String path, RequestMethod method) {
		return new IsRequestMethodPredicate(path, method);
	}

	public static Predicate<RequestHandler> isNotRequestMethod(String path, RequestMethod method) {
		return not(isRequestMethod(path, method));
	}

	@Override
	public boolean apply(RequestHandler input) {
		RequestMapping mapping = AnnotationUtils
				.findAnnotation(input.getHandlerMethod().getMethod(), RequestMapping.class);
		if (mapping != null && mapping.path().length > 0) {
			return hasPath(mapping, path) && hasMethod(mapping, method);
		}
		return false;
	}

	private static boolean hasPath(RequestMapping mapping, String path) {
		for (String mappingPath : mapping.path()) {
			if (mappingPath != null && mappingPath.equals(path)) {
				return true;
			}
		}
		return false;
	}

	private static boolean hasMethod(RequestMapping mapping, RequestMethod method) {
		for (RequestMethod mappingMethod : mapping.method()) {
			if (mappingMethod == method) {
				return true;
			}
		}
		return false;
	}
}

Para utilizarlo:

import static IsRequestMethodPredicate.isNotRequestMethod;

@Bean
public Docket api() {
	return new Docket(DocumentationType.SWAGGER_2)
			.select()
			.apis(isNotRequestMethod("/some/third/party/endpoint", RequestMethod.GET))
			.paths(paths())
			.build();
}

Abrir ventana Terminator maximizada

Me gusta utilizar la combinación de teclas control+shift+t para abrir nuevas ventanas del terminal. Es la que suele venir configurada por defecto.

Para que la ventana se abra ocupando toda la pantalla, sólo hay que editar el fichero $HOME/.config/terminator/config y, en dentro del apartado global_config fijar la propiedad window_state.

[global_config]
  window_state = maximise

Configurar el menú “New Document” en Nautilus

El explorador de archivos Nautilus permite trabajar con ficheros y carpetas en linux. Echaba de menos la posibilidad de crear ficheros vacíos haciendo click con el botón derecho (opción del menú desplegable “New Document”).

Existe una carpeta en nuestro directorio personal “Templates” donde podemos crear diferentes plantillas de ficheros. Al añadir estas, aparece el susodicho menú.

Espero que haya sido útil.

Mockito InOrder y ArgumentCaptor

InOrder

Uso de InOrder permite validar el orden en el que se ejecutan las llamadas a los distintos objetos doble (mock).

@Mock
Repository someRepository;

@Mock
Repository someOtherRepository;

@Test
public void testSomething() {

	InOrder order = Mockito.inOrder(someRepository, someOtherRepository);

	order.verify(someRepository).save(anyString());
	order.verify(someOtherRepository).findByName(anyString());
	order.verify(repository).update(anyString());
	order.verifyNoMoreInteractions();
}

ArgumentCaptor

Uso de ArgumentCaptor permite capturar los argumentos que recibe una llamada a una objeto doble (mock).

@Mock
Repository mockRepository;

@Test
public void testSomething() {

	service.useCase("some_string");
	
	ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
	verify(mockRepository).findByName(argument.capture());
	assertEquals("some_string", argument.getValue());
}

Ocultar endpoints Spring Swagger 2

Es posible que en alguna ocasión no queramos que Swagger genere la documentación de algún endpoint por algún motivo concreto. Estos son los pasos a seguir:

1. Definir una anotación con la que decoraremos todos los métodos de los controladores que se quieran ocultar.

import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface HideApiDocumentation {

}

2. Modificar la configuración de Swagger2 para que ignore los métodos anotados.

import static springfox.documentation.builders.PathSelectors.*;
import static com.google.common.base.Predicates.*;
import static springfox.documentation.builders.RequestHandlerSelectors.withMethodAnnotation;

...
...

@Configuration
@EnableSwagger2
public class SwaggerConfiguration {

	@Bean
	public Docket api() {
		return new Docket(DocumentationType.SWAGGER_2)
			.select()
			.apis(not(withMethodAnnotation(HideApiDocumentation.class)))
			.paths(PathSelectors.any())
			.build();
	}
}

3. Utilizar la anotación en los métodos de los controladores

@RestController
public class SomeController {

	@HideApiDocumentation
	@PostMapping("/")
	public String post(@RequestBody String param) {
		...
	}

}

4. Opcional. Ocultar endpoints de librerías de terceros.

Si queremos ocultar también los enpoints generados por librerías de terceros (p.ej oauth), tendremos que indicar manualmente las rutas (método “paths”).

@Bean
public Docket api() {
	return new Docket(DocumentationType.SWAGGER_2)
		.select()
		.apis(not(withMethodAnnotation(HideApiDocumentation.class)))
		.paths(paths())
		.build();
}

private Predicate<String> paths() {
	return not(or(
	regex("/oauth/token.*"),
	regex("/oauth/revoke.*")));
}

Espero que sea de utilidad.