Model-Based Testing with Testcontainers and Jqwik

When testing complex systems, the more edge cases you can identify, the better your software performs in the real world. But how do you efficiently generate hundreds or thousands of meaningful tests that reveal hidden bugs? Enter model-based testing (MBT), a technique that automates test case generation by modeling your software’s expected behavior.

In this demo, we’ll explore the model-based testing technique to perform regression testing on a simple REST API.

We’ll use the jqwik test engine on JUnit 5 to run property and model-based tests. Additionally, we’ll use Testcontainers to spin up Docker containers with different versions of our application.

Model-based testing

Model-based testing is a method for testing stateful software by comparing the tested component with a model that represents the expected behavior of the system. Instead of manually writing test cases, we’ll use a testing tool that:

Takes a list of possible actions supported by the application

Automatically generates test sequences from these actions, targeting potential edge cases

Executes these tests on the software and the model, comparing the results

In our case, the actions are simply the endpoints exposed by the application’s API. For the demo’s code examples, we’ll use a basic service with a CRUD REST API that allows us to:

Find an employee by their unique employee number

Update an employee’s name

Get a list of all the employees from a department

Register a new employee

Figure 1: Finding an employee, updating their name, finding their department, and registering a new employee.

Once everything is configured and we finally run the test, we can expect to see a rapid sequence of hundreds of requests being sent to the two stateful services:

Figure 2: New requests being sent to the two stateful services.

Docker Compose

Let’s assume we need to switch the database from Postgres to MySQL and want to ensure the service’s behavior remains consistent. To test this, we can run both versions of the application, send identical requests to each, and compare the responses.

We can set up the environment using a Docker Compose that will run two versions of the app:

Model (mbt-demo:postgres): The current live version and our source of truth.

Tested version (mbt-demo:mysql): The new feature branch under test.

services:
## MODEL
app-model:
image: mbt-demo:postgres
# …
depends_on:
– postgres
postgres:
image: postgres:16-alpine
# …

## TESTED
app-tested:
image: mbt-demo:mysql
# …
depends_on:
– mysql
mysql:
image: mysql:8.0
# …

Testcontainers

At this point, we could start the application and databases manually for testing, but this would be tedious. Instead, let’s use Testcontainers’ ComposeContainer to automate this with our Docker Compose file during the testing phase.

In this example, we’ll use jqwik as our JUnit 5 test runner. First, let’s add the jqwik and Testcontainers and the jqwik-testcontainers dependencies to our pom.xml:

<dependency>
<groupId>net.jqwik</groupId>
<artifactId>jqwik</artifactId>
<version>1.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.jqwik</groupId>
<artifactId>jqwik-testcontainers</artifactId>
<version>0.5.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.20.1</version>
<scope>test</scope>
</dependency>

As a result, we can now instantiate a ComposeContainer and pass our test docker-compose file as argument:

@Testcontainers
class ModelBasedTest {

@Container
static ComposeContainer ENV = new ComposeContainer(new File("src/test/resources/docker-compose-test.yml"))
.withExposedService("app-tested", 8080, Wait.forHttp("/api/employees").forStatusCode(200))
.withExposedService("app-model", 8080, Wait.forHttp("/api/employees").forStatusCode(200));

// tests
}

Test HTTP client

Now, let’s create a small test utility that will help us execute the HTTP requests against our services:

class TestHttpClient {
ApiResponse<EmployeeDto> get(String employeeNo) { /* … */ }

ApiResponse<Void> put(String employeeNo, String newName) { /* … */ }

ApiResponse<List<EmployeeDto>> getByDepartment(String department) { /* … */ }

ApiResponse<EmployeeDto> post(String employeeNo, String name) { /* … */ }

record ApiResponse<T>(int statusCode, @Nullable T body) { }

record EmployeeDto(String employeeNo, String name) { }
}

Additionally, in the test class, we can declare another method that helps us create TestHttpClients for the two services started by the ComposeContainer:

static TestHttpClient testClient(String service) {
int port = ENV.getServicePort(service, 8080);
String url = "http://localhost:%s/api/employees".formatted(port);
return new TestHttpClient(service, url);
}

jqwik

Jqwik is a property-based testing framework for Java that integrates with JUnit 5, automatically generating test cases to validate properties of code across diverse inputs. By using generators to create varied and random test inputs, jqwik enhances test coverage and uncovers edge cases.

If you’re new to jqwik, you can explore their API in detail by reviewing the official user guide. While this tutorial won’t cover all the specifics of the API, it’s essential to know that jqwik allows us to define a set of actions we want to test.

To begin with, we’ll use jqwik’s @Property annotation — instead of the traditional @Test — to define a test:

@Property
void regressionTest() {
TestHttpClient model = testClient("app-model");
TestHttpClient tested = testClient("app-tested");
// …
}

Next, we’ll define the actions, which are the HTTP calls to our APIs and can also include assertions.

For instance, the GetOneEmployeeAction will try to fetch a specific employee from both services and compare the responses:

record ModelVsTested(TestHttpClient model, TestHttpClient tested) {}

record GetOneEmployeeAction(String empNo) implements Action<ModelVsTested> {
@Override
public ModelVsTested run(ModelVsTested apps) {
ApiResponse<EmployeeDto> actual = apps.tested.get(empNo);
ApiResponse<EmployeeDto> expected = apps.model.get(empNo);

assertThat(actual)
.satisfies(hasStatusCode(expected.statusCode()))
.satisfies(hasBody(expected.body()));
return apps;
}
}

Additionally, we’ll need to wrap these actions within Arbitrary objects. We can think of Arbitraries as objects implementing the factory design pattern that can generate a wide variety of instances of a type, based on a set of configured rules.

For instance, the Arbitrary returned by employeeNos() can generate employee numbers by choosing a random department from the configured list and concatenating a number between 0 and 200:

static Arbitrary<String> employeeNos() {
Arbitrary<String> departments = Arbitraries.of("Frontend", "Backend", "HR", "Creative", "DevOps");
Arbitrary<Long> ids = Arbitraries.longs().between(1, 200);
return Combinators.combine(departments, ids).as("%s-%s"::formatted);
}

Similarly, getOneEmployeeAction() returns an Aribtrary action based on a given Arbitrary employee number:

static Arbitrary<GetOneEmployeeAction> getOneEmployeeAction() {
return employeeNos().map(GetOneEmployeeAction::new);
}

After declaring all the other Actions and Arbitraries, we’ll create an ActionSequence:

@Provide
Arbitrary<ActionSequence<ModelVsTested>> mbtJqwikActions() {
return Arbitraries.sequences(
Arbitraries.oneOf(
MbtJqwikActions.getOneEmployeeAction(),
MbtJqwikActions.getEmployeesByDepartmentAction(),
MbtJqwikActions.createEmployeeAction(),
MbtJqwikActions.updateEmployeeNameAction()
));
}

static Arbitrary<Action<ModelVsTested>> getOneEmployeeAction() { /* … */ }
static Arbitrary<Action<ModelVsTested>> getEmployeesByDepartmentAction() { /* … */ }
// same for the other actions

Now, we can write our test and leverage jqwik to use the provided actions to test various sequences. Let’s create the ModelVsTested tuple and use it to execute the sequence of actions against it:

@Property
void regressionTest(@ForAll("mbtJqwikActions") ActionSequence<ModelVsTested> actions) {
ModelVsTested testVsModel = new ModelVsTested(
testClient("app-model"),
testClient("app-tested")
);
actions.run(testVsModel);
}

That’s it — we can finally run the test! The test will generate a sequence of thousands of requests trying to find inconsistencies between the model and the tested service:

INFO com.etr.demo.utils.TestHttpClient — [app-tested] PUT /api/employeesFrontend-129?name=v
INFO com.etr.demo.utils.TestHttpClient — [app-model] PUT /api/employeesFrontend-129?name=v
INFO com.etr.demo.utils.TestHttpClient — [app-tested] GET /api/employees/Frontend-129
INFO com.etr.demo.utils.TestHttpClient — [app-model] GET /api/employees/Frontend-129
INFO com.etr.demo.utils.TestHttpClient — [app-tested] POST /api/employees { name=sdxToS, empNo=Frontend-91 }
INFO com.etr.demo.utils.TestHttpClient — [app-model] POST /api/employees { name=sdxToS, empNo=Frontend-91 }
INFO com.etr.demo.utils.TestHttpClient — [app-tested] PUT /api/employeesFrontend-4?name=PZbmodNLNwX
INFO com.etr.demo.utils.TestHttpClient — [app-model] PUT /api/employeesFrontend-4?name=PZbmodNLNwX
INFO com.etr.demo.utils.TestHttpClient — [app-tested] GET /api/employees/Frontend-4
INFO com.etr.demo.utils.TestHttpClient — [app-model] GET /api/employees/Frontend-4
INFO com.etr.demo.utils.TestHttpClient — [app-tested] GET /api/employees?department=ٺ⯟桸
INFO com.etr.demo.utils.TestHttpClient — [app-model] GET /api/employees?department=ٺ⯟桸

Catching errors

If we run the test and check the logs, we’ll quickly spot a failure. It appears that when searching for employees by department with the argument ٺ⯟桸 the model produces an internal server error, while the test version returns 200 OK:

Original Sample
—————
actions:
ActionSequence[FAILED]: 8 actions run [
UpdateEmployeeAction[empNo=Creative-13, newName=uRhplM],
CreateEmployeeAction[empNo=Backend-184, name=aGAYQ],
UpdateEmployeeAction[empNo=Backend-3, newName=aWCxzg],
UpdateEmployeeAction[empNo=Frontend-93, newName=SrJTVwMvpy],
UpdateEmployeeAction[empNo=Frontend-129, newName=v],
CreateEmployeeAction[empNo=Frontend-91, name=sdxToS],
UpdateEmployeeAction[empNo=Frontend-4, newName=PZbmodNLNwX],
GetEmployeesByDepartmentAction[department=ٺ⯟桸]
]
final currentModel: ModelVsTested[model=com.etr.demo.utils.TestHttpClient@5dc0ff7d, tested=com.etr.demo.utils.TestHttpClient@64920dc2]
Multiple Failures (1 failure)
— failure 1 —
expected: 200
but was: 500

Upon investigation, we find that the issue arises from a native SQL query using Postgres-specific syntax to retrieve data. While this was a simple issue in our small application, model-based testing can help uncover unexpected behavior that may only surface after a specific sequence of repetitive steps pushes the system into a particular state.

Wrap up

In this post, we provided hands-on examples of how model-based testing works in practice. From defining models to generating test cases, we’ve seen a powerful approach to improving test coverage and reducing manual effort. Now that you’ve seen the potential of model-based testing to enhance software quality, it’s time to dive deeper and tailor it to your own projects.

Clone the repository to experiment further, customize the models, and integrate this methodology into your testing strategy. Start building more resilient software today!

Thank you to Emanuel Trandafir for contributing this post.

Learn more

Clone the model-based testing practice repo.

Subscribe to the Docker Newsletter. 

Visit the Testcontainers website.

Get started with Testcontainers Cloud by creating a free account.

Have questions? The Docker community is here to help.

New to Docker? Get started.

Quelle: https://blog.docker.com/feed/

Docker at Cloud Expo Asia: GenAI, Security, and New Innovations

Cloud Expo Asia 2024 in Singapore drew thousands of cloud professionals and tech business leaders to explore and exchange the latest in cloud computing, security, GenAI, sustainability, DevOps, and more. At our Cloud Expo Asia booth, Docker showcased our latest innovations in AI integration, containerization, security best practices, and updated product offerings. Here are a few highlights from our experience at the event.

AI/ML and GenAI everywhere

AI/ML and GenAI were hot topics at Cloud Expo Asia. Docker CPO Giri Sreenivas’s talk on Transforming App Development: Docker’s Advanced Containerization and AI Integration highlighted that GenAI impacts software in two big ways — it accelerates product development and creates new types of products and experiences. He discussed how containers are an ideal tool for containerizing GenAI workflows in development, ensuring consistency across CI/CD pipelines and reproducibility across diverse platforms in production.

Docker Chief Product Officer Giri Sreenivas’s talk drew an overflow crowd.

Sreenivas highlighted the Docker extension for GitHub Copilot as an example of how Docker helps empower development teams to focus on innovation — closing the gap from the first line of code to production. Sreenivas also gave a sneak peek into upcoming products designed to streamline GenAI development to illustrate Docker’s commitment to evolving solutions to meet emerging needs. 

Adopting security best practices and shifting left

Developer efficiency and security were also popular themes at the event. When Sreenivas mentioned in his talk that security vulnerabilities that cost dollars to fix early in development would cost hundreds of dollars later in production, members of the audience nodded in agreement.

Docker CTO Justin Cormack gave a keynote address titled “The Docker Effect: Driving Developer Efficiency and Innovation in a Hybrid World.” He discussed how implementing best practices and investing in the inner loop are crucial for today’s development teams. 

One best practice, for example, is shifting left and identifying problems as quickly as possible in the software development lifecycle. This approach improves efficiency and reduces costs by detecting and addressing software issues earlier before they become expensive problems.

At Docker CTO Justin Cormack’s talk, attendees were eager to snap pictures of every slide.

Cormack also provided a few tips for meeting the security and control needs of modern enterprises with a layered approach. Start with key building blocks, he explained, such as trusted content, which provides dev teams with a good foundation to build securely from the start. 

Docker CTO Justin Cormack’s recommendations on meeting the security and control needs of modern enterprises.

At the Docker event booth, we demonstrated Docker Scout, which helps development teams identify, analyze, and remediate security vulnerabilities early in the dev process. Docker Business customers can take advantage of enterprise controls, letting admins, IT teams, and security teams continuously monitor and manage risk and compliance with confidence. 

After four hours of demos at the Docker booth, senior software engineer Chase Frankenfeld was still enthusiastically discussing Docker products, while our CEO Scott Johnston listened attentively to an attendee’s questions.

New Docker innovations and updated plan

From students to C-level executives who visited our booth, everyone was eager to learn more about containers and Docker. People lined up to see an end-to-end demo of how the suite of Docker products, such as Docker Desktop, Docker Hub, Docker Build Cloud, and Docker Scout, work together seamlessly to enable development teams to work more efficiently. 

Attendees also had the opportunity to learn more about Docker’s updated plans, which makes accessing the full suite of Docker products and solutions easy, with options for individual developers, small teams, and large enterprises.

Senior software engineer Maxime Clement explains Docker’s updated plans and demos Docker products to booth visitors.

Thanks, Cloud Expo Asia!

We enjoyed our conversations with event attendees and appreciate everyone who helped make this such a successful event. Thank you to the organizers, speakers, sponsors, and the community for a productive, information-packed experience.

What’s better than Docker swag? Docker swag in a claw machine.

From accelerating app development, supporting best practices of shifting left, meeting the security and control needs of modern enterprises, and innovating with GenAI, Docker wants to be your trusted partner to navigate the challenges in modern app development. 

Explore our Docker updated plans to learn how Docker can empower your teams, or contact our sales team to discover how we can help you innovate with confidence.

Learn more

Find out how we’re investing in innovation and customer relationships.

Read about updated Docker plans: Simpler, More Value, Better Development and Productivity.

Subscribe to the Docker Newsletter. 

Quelle: https://blog.docker.com/feed/

Using Docker AI Tools for Devs to Provide Context for Better Code Fixes

This ongoing Docker Labs GenAI series explores the exciting space of AI developer tools. At Docker, we believe there is a vast scope to explore, openly and without the hype. We will share our explorations and collaborate with the developer community in real-time. Although developers have adopted autocomplete tooling like GitHub Copilot and use chat, there is significant potential for AI tools to assist with more specific tasks and interfaces throughout the entire software lifecycle. Therefore, our exploration will be broad. We will be releasing software as open source so you can play, explore, and hack with us, too.

At Docker Labs, we’ve been exploring how LLMs can connect different parts of the developer workflow, bridging gaps between tools and processes. A key insight is that LLMs excel at fixing code issues when they have the right context. To provide this context, we’ve developed a process that maps out the codebase using linting violations and the structure of top-level code blocks. 

By combining these elements, we teach the LLM to construct a comprehensive view of the code, enabling it to fix issues more effectively. By leveraging containerization, integrating these tools becomes much simpler.

Previously, my linting process felt a bit disjointed. I’d introduce an error, run Pylint, and receive a message that was sometimes cryptic, forcing me to consult Pylint’s manual to understand the issue. When OpenAI released ChatGPT, the process improved slightly. I could run Pylint, and if I didn’t grasp an error message, I’d copy the code and the violation into GPT to get a better explanation. Sometimes, I’d ask it to fix the code and then manually paste the solution back into my editor.

However, this approach still required several manual steps: copying code, switching between applications, and integrating fixes. How might we improve this process?

Docker’s AI Tools for Devs prompt runner is an architecture that allows us to integrate tools like Pylint directly into the LLM’s workflow through containerization. By containerizing Pylint and creating prompts that the LLM can use to interact with it, we’ve developed a system where the LLM can access the necessary tools and context to help fix code issues effectively.

Understanding the cognitive architecture

For the LLM to assist effectively, it needs a structured way of accessing and processing information. In our setup, the LLM uses the Docker prompt runner to interact with containerized tools and the codebase. The project context is extracted using tools such as Pylint and Tree-sitter that run against the project. This context is then stored and managed, allowing the LLM to access it when needed.

By having access to the codebase, linting tools, and the context of previous prompts, the LLM can understand where problems are, what they are, and have the right code fragments to fix them. This setup replaces the manual process of finding issues and feeding them to the LLM with something automatic and more engaging.

Streamlining the workflow

Now, within my workflow, I can ask the assistant about code quality and violations directly. The assistant, powered by an LLM, has immediate access to a containerized Pylint tool and a database of my code through the Docker prompt runner. This integration allows the LLM to use tools to assist me directly during development, making the programming experience more efficient.

This approach helps us rethink how we interact with our tools. By enabling a conversational interface with tools that map code to issues, we’re exploring possibilities for a more intuitive development experience. Instead of manually finding problems and feeding them to an AI, we can convert our relationship with tools themselves to be conversational partners that can automatically detect issues, understand the context, and provide solutions.

Walking through the prompts

Our project is structured around a series of prompts that guide the LLM through the tasks it needs to perform. These prompts are stored in a Git repository and can be versioned, tracked, and shared. They form the backbone of the project, allowing the LLM to interact with tools and the codebase effectively. We automate this entire process using Docker and a series of prompts stored in a Git repository. Each prompt corresponds to a specific task in the workflow, and Docker containers ensure a consistent environment for running tools and scripts.

Workflow steps

An immediate and existential challenge we encountered was that this class of problem has a lot of opportunities to overwhelm the context of the LLM. Want to read a source code file? It has to be small enough to read. Need to work on more than one file? Your realistic limit is three to four files at once. To solve this, we can instruct the LLM to automate its own workflow with tools, where each step runs in a Docker container.

Again, each step in this workflow runs in a Docker container, which ensures a consistent and isolated environment for running tools and scripts. The first four steps prepare the agent to be able to extract the right context for fixing violations. Once the agent has the necessary context, the LLM can effectively fix the code issues in step 5.

1. Generate violations report using Pylint:

Run Pylint to produce a violation report.

2. Create a SQLite database:

Set up the database schema to store violation data and code snippets.

3. Generate and run INSERT statements:

Decouple violations from the range they represent.

Use a script to convert every violation and range from the report into SQL insert statements.

Run the statements against the database to populate it with the necessary data.

4. Index code in the database:

Generate an abstract syntax tree (AST) of the project with Tree-sitter (Figure 1).

Figure 1: Generating an abstract syntax tree.

Find all second-level nodes (Figure 2). In Python’s grammar, second-level nodes are statements inside of a module.

Figure 2: Extracting content for the database.

Index these top-level ranges into the database.

Populate a new table to store the source code at these top-level ranges.

5. Fix violations based on context:

Once the agent has gathered and indexed the necessary context, use prompts to instruct the LLM to query the database and fix the code issues (Figure 3).

Figure 3: Instructions for fixing violations.

Each step from 1 to 4 builds the foundation for step 5, where the LLM, with the proper context, can effectively fix violations. The structured preparation ensures that the LLM has all the information it needs to address code issues with precision.

Refining the context for LLM fixes

To understand how our system improves code fixes, let’s consider a specific violation flagged by Pylint. Say we receive a message that there’s a violation on line 60 of our code file block_listed_name.py:

{
"type": "convention",
"module": "block_listed_name",
"obj": "do_front",
"line": 60,
"column": 4,
"endLine": 60,
"endColumn": 7,
"path": "cloned_repo/naming_conventions/block_listed_name.py",
"symbol": "disallowed-name",
"message": "Disallowed name "foo"",
"message-id": "C0104"
}

From this Pylint violation, we know that the variable foo is a disallowed name. However, if we tried to ask the LLM to fix this issue based solely on this snippet of information, the response wouldn’t be as effective. Why? The LLM lacks the surrounding context — the full picture of the function in which this violation occurs.

This is where indexing the codebase becomes essential

Because we’ve mapped out the codebase, we can now ask the LLM to query the index and retrieve the surrounding code that includes the do_front function. The LLM can even generate the SQL query for us, thanks to its knowledge of the database schema. Once we’ve retrieved the full function definition, the LLM can work with a more complete view of the problem:

def do_front(front_filename, back_filename):
"""
Front strategy: loop over front image,
detect blue pixels there,
substitute in pixels from back.
Return changed front image.
"""
foo = SimpleImage(front_filename)
back = SimpleImage(back_filename)
for y in range(foo.height):xc
for x in range(foo.width):
pixel = foo.get_pixel(x, y)
# Detect blue pixels in front and replace with back pixels
if pixel[2] > 2 * max(pixel[0], pixel[1]):
back_pixel = back.get_pixel(x, y)
foo.set_pixel(x, y, back_pixel)
return foo

Now that the LLM can see the whole function, it’s able to propose a more meaningful fix. Here’s what it suggests after querying the indexed codebase and running the fix:

def do_front(front_filename, back_filename):
"""
Front strategy: loop over front image,
detect blue pixels there,
substitute in pixels from back.
Return changed front image.
"""
front_image = SimpleImage(front)
back_image = SimpleImage(back_filename)
for y in range(front_image.height):
for x in range(front_image.width pixel = front_image.get_pixel(x y)
# Detect blue pixels in front and replace with back pixels
if pixel[2 > * max(pixel[0 pixel[1]):
back_pixel = back_image.get_pixel(x,)
front_image.set_pixel(x,, back_pixel)
return front_image

Here, the variable foo has been replaced with the more descriptive front_image, making the code more readable and understandable. The key step was providing the LLM with the correct level of detail — the top-level range — instead of just a single line or violation message. With the right context, the LLM’s ability to fix code becomes much more effective, which ultimately streamlines the development process.

Remember, all of this information is retrieved and indexed by the LLM itself through the prompts we’ve set up. Through this series of prompts, we’ve reached a point where the assistant has a comprehensive understanding of the codebase. 

At this stage, not only can I ask for a fix, but I can even ask questions like “what’s the violation at line 60 in naming_conventions/block_listed_name.py?” and the assistant responds with:

On line 60 of naming_conventions/block_listed_name.py, there's a violation: Disallowed name 'foo'. The variable name 'foo' is discouraged because it doesn't convey meaningful information about its purpose.

Although Pylint has been our focus here, this approach points to a new conversational way to interact with many tools that map code to issues. By integrating LLMs with containerized tools through architectures like the Docker prompt runner, we can enhance various aspects of the development workflow.

We’ve learned that combining tool integration, cognitive preparation of the LLM, and a seamless workflow can significantly improve the development experience. This integration allows an LLM to use tools to directly help while developing, and while Pylint has been the focus here, this also points to a new conversational way to interact with many tools that map code to issues.

To follow along with this effort, check out the GitHub repository for this project.

For more on what we’re doing at Docker, subscribe to our newsletter.

Learn more

Read the Docker Labs GenAI series.

Subscribe to the Docker Newsletter. 

Get the latest release of Docker Desktop.

Have questions? The Docker community is here to help.

New to Docker? Get started.

Quelle: https://blog.docker.com/feed/

Announcing IBM Granite AI Models Now Available on Docker Hub

We are thrilled to announce that Granite models, IBM’s family of open source and proprietary models built for business, as well as Red Hat InstructLab model alignment tools, are now available on Docker Hub. 

Now, developer teams can easily access, deploy, and scale applications using IBM’s AI models specifically designed for developers.

This news will be officially announced during the AI track of the keynote at IBM TechXchange on October 22. Attendees will get an exclusive look at how IBM’s Granite models on Docker Hub accelerate AI-driven application development across multiple programming languages.

Why Granite on Docker Hub?

With a principled approach to data transparency, model alignment, and security, IBM’s open source Granite models represent a significant leap forward in natural language processing. The models are available under an Apache 2.0 license, empowering developer teams to bring generative AI into mission-critical applications and workflows. 

Granite models deliver superior performance in coding and targeted language tasks at lower latencies, all while requiring a fraction of the compute resources and reducing the cost of inference. This efficiency allows developers to experiment, build, and scale generative AI applications both on-premises and in the cloud, all within departmental budgetary limits.

Here’s what this means for you:

Simplified deployment: Pull the Granite image from Docker Hub and get up and running in minutes.

Scalability: Docker offers a lightweight and efficient method for scaling artificial intelligence and machine learning (AI/ML) applications. It allows you to run multiple containers on a single machine or distribute them across different machines in a cluster, enabling horizontal scalability.

Flexibility: Customize and extend the model to suit your specific needs without worrying about underlying infrastructure.

Portability: By creating Docker images once and deploying them anywhere, you eliminate compatibility problems and reduce the need for configurations. 

Community support: Leverage the vast Docker and IBM communities for support, extensions, and collaborations.

In addition to the IBM Granite models, Red Hat also made the InstructLab model alignment tools available on Docker Hub. Developers using InstructLab can adapt pre-trained LLMs using far less real-world data and computing resources than alternative methodologies. InstructLab is model-agnostic and can be used to fine-tune any LLM of your choice by providing additional skills and knowledge.

With IBM Granite AI models and InstructLab available on Docker Hub, Docker and IBM enable easy integration into existing environments and workflows.

Getting started with Granite

You can find the following images available on Docker Hub:

InstructLab: Ideal for desktop or Mac users looking to explore InstructLab, this image provides a simple introduction to the platform without requiring specialized hardware. It’s perfect for prototyping and testing before scaling up.

InstructLab with CUDA support: Designed for running full training workflows on GPU-equipped Linux servers, this image accelerates the synthetic data generation and training process by leveraging NVIDIA GPUs.

Granite-7b-lab: This image is optimized for model serving and inference on desktop or Mac environments, using the Granite-7B model. It allows for efficient and scalable inference tasks without needing a GPU, perfect for smaller-scale deployments or local testing.

Granite-7b-lab with CUDA support: For those with GPU-equipped Linux servers, this image supports faster model inference and serving through CUDA acceleration. This is ideal for high-performance AI applications where response times and throughput are critical.

How to pull and run IBM Granite images from Docker Hub 

IBM Granite provides a toolset for building and managing cloud-native applications. Follow these steps to pull and run an IBM Granite image using Docker and the CLI. You can follow similar steps for the Red Hat InstructLab images.

Authenticate to Docker Hub

Enter your Docker username and password when prompted.

Pull the IBM Granite Image

Pull the IBM Granite image from Docker Hub. There are two versions of the image: 

redhat/granite-7b-lab-gguf: For Mac/desktop users with no GPU support

redhat/granite-7b-lab-gguf-cuda: For Linux NVIDIA® CUDA® support

Run the Image in a Container

Start a container with the IBM Granite image. The container can be started in two modes: CLI (default) and server.

To start the container in CLI mode, run the following:docker run –ipc=host -it redhat/granite-7b-lab-gguf 

This command opens an interactive bash session within the container, allowing you to use the tools.

To run the container in server mode, run the following command:

docker run –ipc=host -it redhat/granite-7b-lab-gguf -s

You can check IBM Granite’s documentation for details on using IBM Granite Models.

Join us at IBM TechXchange

Granite on Docker Hub will be officially announced at the IBM TechXchange Conference, which will be held October 21-24 in Las Vegas. Our head of technical alliances, Eli Aleyner, will show a live demonstration at the AI track of the keynote during IBM TechXchange. Oleg Šelajev, Docker’s staff developer evangelist, will show how app developers can test their GenAI apps with local models. Additionally, you’ll learn how Docker’s collaboration with Red Hat is improving developer productivity.

The availability of Granite on Docker Hub marks a significant milestone in making advanced AI models accessible to all. We’re excited to see how developer teams will harness the power of Granite to innovate and solve complex challenges.

Stay anchored for more updates, and as always, happy coding!

Learn more

Read the Docker Labs GenAI series.

Subscribe to the Docker Newsletter. 

What is InstructLab?

What are Granite Models?

Accelerating AI Development with IBM Granite AI Models and Docker — IBM TechXchange session with Eli Aleyner.

Developer productivity for apps with AI – IBM TechXchange session with Oleg Šelajev.

Quelle: https://blog.docker.com/feed/

New Docker Terraform Provider: Automate, Secure, and Scale with Ease

We’re excited to announce the launch of the Docker Terraform Provider, designed to help users and organizations automate and securely manage their Docker-hosted resources. This includes repositories, teams, organization settings, and more, all using Terraform’s infrastructure-as-code approach. This provider brings a unified, scalable, and secure solution for managing Docker resources in an automated fashion — whether you’re managing a single repository or a large-scale organization.

A new way of working with Docker Hub

The Docker Terraform Provider introduces a new way of working with Docker Hub, enabling infrastructure-as-code best practices that are already widely adopted across cloud-native environments. By integrating Docker Hub with Terraform, organizations can streamline resource management, improve security, and collaborate more effectively, all while ensuring Docker resources remain in sync with other infrastructure components.

The Problem

Managing Docker Hub resources manually can become cumbersome and prone to errors, especially as teams grow and projects scale. Maintaining configurations can lead to inconsistencies, reduced security, and a lack of collaboration between teams without a streamlined, version-controlled system. The Docker Terraform Provider solves this by allowing you to manage Docker Hub resources in the same way you manage your other cloud resources, ensuring consistency, auditability, and automation across the board.

The solution

The Docker Terraform Provider offers:

Unified management: With this provider, you can manage Docker repositories, teams, users, and organizations in a consistent workflow, using the same code and structure across environments.

Version control: Changes to Docker Hub resources are captured in your Terraform configuration, providing a version-controlled, auditable way to manage your Docker infrastructure.

Collaboration and automation: Teams can now collaborate seamlessly, automating the provisioning and management of Docker Hub resources with Terraform, enhancing productivity and ensuring best practices are followed.

Scalability: Whether you’re managing a few repositories or an entire organization, this provider scales effortlessly to meet your needs.

Example

At Docker, even we faced challenges managing our Docker Hub resources, especially when adding repositories without owner permissions — it was a frustrating, manual process. With the Terraform provider, anyone in the company can create a new repository without having elevated Docker Hub permissions. All levels of employees are now empowered to write code rather than track down coworkers. This streamlines developer workflows with familiar tooling and reduces employee permissions. Security and developers are happy!

Here’s an example where we are managing a repository, an org team, the permissions for the created repo, and a PAT token:

terraform {
required_providers {
docker = {
source = "docker/docker"
version = "~> 0.2"
}
}
}

# Initialize provider
provider "docker" {}

# Define local variables for customization
locals {
namespace = "my-docker-namespace"
repo_name = "my-docker-repo"
org_name = "my-docker-org"
team_name = "my-team"
my_team_users = ["user1", "user2"]
token_label = "my-pat-token"
token_scopes = ["repo:read", "repo:write"]
permission = "admin"
}

# Create repository
resource "docker_hub_repository" "org_hub_repo" {
namespace = local.namespace
name = local.repo_name
description = "This is a generic Docker repository."
full_description = "Full description for the repository."
}

# Create team
resource "docker_org_team" "team" {
org_name = local.org_name
team_name = local.team_name
team_description = "Team description goes here."
}

# Team association
resource "docker_org_team_member" "team_membership" {
for_each = toset(local.my_team_users)

org_name = local.org_name
team_name = docker_org_team.team.team_name
user_name = each.value
}

# Create repository team permission
resource "docker_hub_repository_team_permission" "repo_permission" {
repo_id = docker_hub_repository.org_hub_repo.id
team_id = docker_org_team.team.id
permission = local.permission
}

# Create access token
resource "docker_access_token" "access_token" {
token_label = local.token_label
scopes = local.token_scopes
}

Future work

We’re just getting started with the Docker Terraform Provider, and there’s much more to come. Future work will expand support to other products in Docker’s suite, including Docker Scout, Docker Build Cloud, and Testcontainers Cloud. Stay tuned as we continue to evolve and enhance the provider with new features and integrations.

For feedback and issue tracking, visit the official Docker Terraform Provider repository or submit feedback via our issue tracker.

We’re confident this new provider will enhance how teams work with Docker Hub, making it easier to manage, secure, and scale their infrastructure while focusing on what matters most — building great software.

Learn more

Visit the official Docker Terraform Provider repository.

Submit feedback via our issue tracker.

Subscribe to the Docker Newsletter. 

Get the latest release of Docker Desktop.

Have questions? The Docker community is here to help.

New to Docker? Get started.

Quelle: https://blog.docker.com/feed/

How Docker IT Streamlined Docker Desktop Deployment Across the Global Team

At Docker, innovation and efficiency are integral to how we operate. When our own IT team needed to deploy Docker Desktop to various teams, including non-engineering roles like customer support and technical sales, the existing process was functional but manual and time-consuming. Recognizing the need for a more streamlined and secure approach, we leveraged new Early Access (EA) Docker Business features to refine our deployment strategy.

A seamless deployment process

Faced with the challenge of managing diverse requirements across the organization, we knew it was time to enhance our deployment methods.

The Docker IT team transitioned from using registry.json files to a more efficient method involving registry keys and new MSI installers for Windows, along with configuration profiles and PKG installers for macOS. This transition simplified deployment, provided better control for admins, and allowed for faster rollouts across the organization.

“From setup to deployment, it took 24 hours. We started on a Monday morning, and by the next day, it was done,” explains Jeffrey Strauss, Head of Docker IT. 

Enhancing security and visibility

Security is always a priority. By integrating login enforcement with single sign-on (SSO) and System for Cross-domain Identity Management (SCIM), Docker IT ensured centralized control and compliance with security policies. The Docker Desktop Insights Dashboard (EA) offered crucial visibility into how Docker Desktop was being used across the organization. Admins could now see which versions were installed and monitor container usage, enabling informed decisions about updates, resource allocation, and compliance. (Docker Business customers can learn more about access and timelines by contacting their account reps. The Insights Dashboard is only available to Docker Business customers with enforced authentication for organization users.)

Steven Novick, Docker’s Principal Product Manager, emphasized, “With the new solution, deployment was simpler and tamper-proof, giving a clear picture of Docker usage within the organization.”

Benefits beyond deployment

The improvements made by Docker IT extended beyond just deployment efficiency:

Improved visibility: The Insights Dashboard provided detailed data on Docker usage, helping ensure all users are connected to the organization.

Efficient deployment: Docker Desktop was deployed to hundreds of computers within 24 hours, significantly reducing administrative overhead.

Enhanced security: Centralized control to enforce authentication via MDM tools like Intune for Windows and Jamf for macOS strengthened security and compliance.

Seamless user experience: Early and transparent communication ensured a smooth transition, minimizing disruptions.

Looking ahead

The successful deployment of Docker Desktop within 24 hours demonstrates Docker’s commitment to continuous improvement and innovation. We are excited about the future developments in Docker Desktop management and look forward to supporting our customers as they achieve their goals with Docker. 

Existing Docker Business customers can learn more about access and timelines by contacting their account reps. The Insights Dashboard is only available in Early Access to select Docker Business customers with enforced authentication for organization users.

Curious about how Docker’s new features can benefit your team? Get in touch to discover more or explore our customer stories to see how others are succeeding with Docker.

Learn more

Read the Docker IT case study to learn more.

Subscribe to the Docker Newsletter. 

Get the latest release of Docker Desktop.

Have questions? The Docker community is here to help.

New to Docker? Get started.

Quelle: https://blog.docker.com/feed/

Docker Best Practices: Using ARG and ENV in Your Dockerfiles

If you’ve worked with Docker for any length of time, you’re likely accustomed to writing or at least modifying a Dockerfile. This file can be thought of as a recipe for a Docker image; it contains both the ingredients (base images, packages, files) and the instructions (various RUN, COPY, and other commands that help build the image).

In most cases, Dockerfiles are written once, modified seldom, and used as-is unless something about the project changes. Because these files are created or modified on such an infrequent basis, developers tend to rely on only a handful of frequently used instructions — RUN, COPY, and EXPOSE being the most common. Other instructions can enhance your image, making it more configurable, manageable, and easier to maintain. 

In this post, we will discuss the ARG and ENV instructions and explore why, how, and when to use them.

ARG: Defining build-time variables

The ARG instruction allows you to define variables that will be accessible during the build stage but not available after the image is built. For example, we will use this Dockerfile to build an image where we make the variable specified by the ARG instruction available during the build process.

FROM ubuntu:latest
ARG THEARG="foo"
RUN echo $THEARG
CMD ["env"]

If we run the build, we will see the echo foo line in the output:

$ docker build –no-cache -t argtest .
[+] Building 0.4s (6/6) FINISHED docker:desktop-linux
<– SNIP –>
=> CACHED [1/2] FROM docker.io/library/ubuntu:latest@sha256:8a37d68f4f73ebf3d4efafbcf66379bf3728902a8038616808f04e 0.0s
=> => resolve docker.io/library/ubuntu:latest@sha256:8a37d68f4f73ebf3d4efafbcf66379bf3728902a8038616808f04e34a9ab6 0.0s
=> [2/2] RUN echo foo 0.1s
=> exporting to image 0.0s
<– SNIP –>

However, if we run the image and inspect the output of the env command, we do not see THEARG:

$ docker run –rm argtest
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=d19f59677dcd
HOME=/root

ENV: Defining build and runtime variables

Unlike ARG, the ENV command allows you to define a variable that can be accessed both at build time and run time:

FROM ubuntu:latest
ENV THEENV="bar"
RUN echo $THEENV
CMD ["env"]

If we run the build, we will see the echo bar line in the output:

$ docker build -t envtest .
[+] Building 0.8s (7/7) FINISHED docker:desktop-linux
<– SNIP –>
=> CACHED [1/2] FROM docker.io/library/ubuntu:latest@sha256:8a37d68f4f73ebf3d4efafbcf66379bf3728902a8038616808f04e 0.0s
=> => resolve docker.io/library/ubuntu:latest@sha256:8a37d68f4f73ebf3d4efafbcf66379bf3728902a8038616808f04e34a9ab6 0.0s
=> [2/2] RUN echo bar 0.1s
=> exporting to image 0.0s
<– SNIP –>

If we run the image and inspect the output of the env command, we do see THEENV set, as expected:

$ docker run –rm envtest
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=f53f1d9712a9
THEENV=bar
HOME=/root

Overriding ARG

A more advanced use of the ARG instruction is to serve as a placeholder that is then updated at build time:

FROM ubuntu:latest
ARG THEARG
RUN echo $THEARG
CMD ["env"]

If we build the image, we see that we are missing a value for $THEARG:

$ docker build -t argtest .
<– SNIP –>
=> CACHED [1/2] FROM docker.io/library/ubuntu:latest@sha256:8a37d68f4f73ebf3d4efafbcf66379bf3728902a8038616808f04e 0.0s
=> => resolve docker.io/library/ubuntu:latest@sha256:8a37d68f4f73ebf3d4efafbcf66379bf3728902a8038616808f04e34a9ab6 0.0s
=> [2/2] RUN echo $THEARG 0.1s
=> exporting to image 0.0s
=> => exporting layers 0.0s
<– SNIP –>

However, we can pass a value for THEARG on the build command line using the –build-arg argument. Notice that we now see THEARG has been replaced with foo in the output:

=> CACHED [1/2] FROM docker.io/library/ubuntu:latest@sha256:8a37d68f4f73ebf3d4efafbcf66379bf3728902a8038616808f04e 0.0s
=> => resolve docker.io/library/ubuntu:latest@sha256:8a37d68f4f73ebf3d4efafbcf66379bf3728902a8038616808f04e34a9ab6 0.0s
=> [2/2] RUN echo foo 0.1s
=> exporting to image 0.0s
=> => exporting layers 0.0s
<– SNIP –>

The same can be done in a Docker Compose file by using the args key under the build key. Note that these can be set as a mapping (THEARG: foo) or a list (- THEARG=foo):

services:
argtest:
build:
context: .
args:
THEARG: foo

If we run docker compose up –build, we can see the THEARG has been replaced with foo in the output:

$ docker compose up –build
<– SNIP –>
=> [argtest 1/2] FROM docker.io/library/ubuntu:latest@sha256:8a37d68f4f73ebf3d4efafbcf66379bf3728902a8038616808f04 0.0s
=> => resolve docker.io/library/ubuntu:latest@sha256:8a37d68f4f73ebf3d4efafbcf66379bf3728902a8038616808f04e34a9ab6 0.0s
=> CACHED [argtest 2/2] RUN echo foo 0.0s
=> [argtest] exporting to image 0.0s
=> => exporting layers 0.0s
<– SNIP –>
Attaching to argtest-1
argtest-1 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
argtest-1 | HOSTNAME=d9a3789ac47a
argtest-1 | HOME=/root
argtest-1 exited with code 0

Overriding ENV

You can also override ENV at build time; this is slightly different from how ARG is overridden. For example, you cannot supply a key without a value with the ENV instruction, as shown in the following example Dockerfile:

FROM ubuntu:latest
ENV THEENV
RUN echo $THEENV
CMD ["env"]

When we try to build the image, we receive an error:

$ docker build -t envtest .
[+] Building 0.0s (1/1) FINISHED docker:desktop-linux
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 98B 0.0s
Dockerfile:3
——————–
1 | FROM ubuntu:latest
2 |
3 | >>> ENV THEENV
4 | RUN echo $THEENV
5 |
——————–
ERROR: failed to solve: ENV must have two arguments

However, we can remove the ENV instruction from the Dockerfile:

FROM ubuntu:latest
RUN echo $THEENV
CMD ["env"]

This allows us to build the image:

$ docker build -t envtest .
<– SNIP –>
=> [1/2] FROM docker.io/library/ubuntu:latest@sha256:8a37d68f4f73ebf3d4efafbcf66379bf3728902a8038616808f04e34a9ab6 0.0s
=> => resolve docker.io/library/ubuntu:latest@sha256:8a37d68f4f73ebf3d4efafbcf66379bf3728902a8038616808f04e34a9ab6 0.0s
=> CACHED [2/2] RUN echo $THEENV 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
<– SNIP –>

Then we can pass an environment variable via the docker run command using the -e flag:

$ docker run –rm -e THEENV=bar envtest
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=638cf682d61f
THEENV=bar
HOME=/root

Although the .env file is usually associated with Docker Compose, it can also be used with docker run.

$ cat .env
THEENV=bar

$ docker run –rm –env-file ./.env envtest
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=59efe1003811
THEENV=bar
HOME=/root

This can also be done using Docker Compose by using the environment key. Note that we use the variable format for the value:

services:
envtest:
build:
context: .
environment:
THEENV: ${THEENV}

If we do not supply a value for THEENV, a warning is thrown:

$ docker compose up –build
WARN[0000] The "THEENV" variable is not set. Defaulting to a blank string.
<– SNIP –>
=> [envtest 1/2] FROM docker.io/library/ubuntu:latest@sha256:8a37d68f4f73ebf3d4efafbcf66379bf3728902a8038616808f04 0.0s
=> => resolve docker.io/library/ubuntu:latest@sha256:8a37d68f4f73ebf3d4efafbcf66379bf3728902a8038616808f04e34a9ab6 0.0s
=> CACHED [envtest 2/2] RUN echo ${THEENV} 0.0s
=> [envtest] exporting to image 0.0s
<– SNIP –>
✔ Container dd-envtest-1 Recreated 0.1s
Attaching to envtest-1
envtest-1 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
envtest-1 | HOSTNAME=816d164dc067
envtest-1 | THEENV=
envtest-1 | HOME=/root
envtest-1 exited with code 0

The value for our variable can be supplied in several different ways, as follows:

On the compose command line:

$ THEENV=bar docker compose up

[+] Running 2/0
✔ Synchronized File Shares 0.0s
✔ Container dd-envtest-1 Recreated 0.1s
Attaching to envtest-1
envtest-1 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
envtest-1 | HOSTNAME=20f67bb40c6a
envtest-1 | THEENV=bar
envtest-1 | HOME=/root
envtest-1 exited with code 0

In the shell environment on the host system:

$ export THEENV=bar
$ docker compose up

[+] Running 2/0
✔ Synchronized File Shares 0.0s
✔ Container dd-envtest-1 Created 0.0s
Attaching to envtest-1
envtest-1 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
envtest-1 | HOSTNAME=20f67bb40c6a
envtest-1 | THEENV=bar
envtest-1 | HOME=/root
envtest-1 exited with code 0

In the special .env file:

$ cat .env
THEENV=bar

$ docker compose up

[+] Running 2/0
✔ Synchronized File Shares 0.0s
✔ Container dd-envtest-1 Created 0.0s
Attaching to envtest-1
envtest-1 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
envtest-1 | HOSTNAME=20f67bb40c6a
envtest-1 | THEENV=bar
envtest-1 | HOME=/root
envtest-1 exited with code 0

Finally, when running services directly using docker compose run, you can use the -e flag to override the .env file.

$ docker compose run -e THEENV=bar envtest

[+] Creating 1/0
✔ Synchronized File Shares 0.0s
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=219e96494ddd
TERM=xterm
THEENV=bar
HOME=/root

The tl;dr

If you need to access a variable during the build process but not at runtime, use ARG. If you need to access the variable both during the build and at runtime, or only at runtime, use ENV.

To decide between them, consider the following flow (Figure 1):

Both ARG and ENV can be overridden from the command line in docker run and docker compose, giving you a powerful way to dynamically update variables and build flexible workflows.

Learn more

Discover more Docker Best Practices.

Subscribe to the Docker Newsletter. 

Get the latest release of Docker Desktop.

Have questions? The Docker community is here to help.

New to Docker? Get started.

Quelle: https://blog.docker.com/feed/

Introducing Organization Access Tokens

In the past, securely managing access to organization resources has been difficult. The only way to gain access has been through an assigned user’s personal access tokens. Whether these users are your engineer’s accounts, bot accounts, or service accounts, they often become points of risk for your organization.

Now, we’re pleased to introduce a long-awaited feature: organization access tokens.

Organization access tokens are like personal access tokens, but at an organizational level with many improvements and features. In this post, we walk through a few reasons why this feature release is so exciting.

Frictionless management

Every day, we are reducing the friction for organizations and engineers using our products. We want you working on your projects, not managing your development tools. 

Organization access tokens do not require you to manage groups and repository assignments like users require. This means you benefit from a straightforward way to manage access that each access token has instead of managing users and their placement within the organization.

If your organization has SSO enabled and enforced, you have likely run into the issue where machine or service accounts cannot log in easily because they don’t have the ability to log into your identity provider. With organization access tokens, this is no longer a problem.Did someone leave your organization? No problem! With organization access tokens, you are still in control of the token instead of having to track down which tokens were on that user’s account and deal with the resulting challenges.

Fine-grained access

Organization access tokens introduce a new way to allow for tokens to access resources within your organization. These tokens can be assigned to specific repositories with specific actions for full access management with “least privilege” applied. Of course, you can also allow access to all resources in your organization.

Expirations

Another critical feature is the ability to set expirations for your organization access tokens. This is great for customers who have compliance requirements for token rotation or for those who just like the extra security.

Visibility

Management and registry actions all show up in your organization’s activity logs for each access token. Each token’s usage also shows up on your organization’s usage reports.

Business use cases and fair use

We believe that organization access tokens are useful in the context of teams and companies, which is why we are making them available to Docker Team and Docker Business subscribers. With the usual attention to the security aspect, avoiding any “misuse” related to the proliferation of the number of access tokens created, we are introducing a limitation in the maximum number of organization access tokens based on the type of subscription. There will be a limit of 10 for Team plans and 100 for Business plans.

Try organization access tokens

If you are on a team or business subscription, check out our documentation to learn more about using organization access tokens.

Learn more

Read the organization access tokens documentation.

Learn about Docker subscription plans.

Subscribe to the Docker Newsletter.

Authenticate and update to receive your subscription level’s newest Docker features.

New to Docker? Create an account.

Quelle: https://blog.docker.com/feed/

How to Improve Your DevOps Automation

DevOps brings together developers and operations teams to create better software by introducing organizational principles that encourage communication, collaboration, innovation, speed, security, and agility throughout the software development lifecycle. And, the popularity and adoption rates of DevOps continue to grow, with 83% of 10,000 global developers surveyed saying that they use the principles, according to an April 2024 report commissioned by the Continuous Delivery Foundation (CDF), a Linux Foundation project.

DevOps includes everything from continuous integration/improvement and continuous deployment/delivery (CI/CD) as code is created and modified, to critical automation capabilities covering a wide range of development processes. Also built into DevOps principles is a focus on creating better applications from code conception all the way through to end-user experiences. Before this unified framework existed, code typically was created in separate silos that did not easily allow collaboration or foster efficient management, speed, or quality. These conditions eventually inspired the DevOps framework and principles.  

DevOps principles and practices also help organizations by constantly integrating user feedback regarding application features, shortcomings, and code glitches, thereby reducing security and operational risks in code as it reaches production.

This blog post aims to help enterprises focus on one of these critical DevOps capabilities in particular — the use of automation to speed and streamline processes across the development lifecycle of applications — to further expand and drive the benefits of using DevOps processes within an organization.

As DevOps use continues to grow, more developers are finding that the Docker containerization platform integrates well as a crucial component of DevOps practices, especially due to its built-in automation features and capabilities.

What is DevOps automation?

DevOps automation is a major time-saver for developers and operations teams because it automates labor-intensive and repetitive processes that can free up developers to instead work on new code innovations and ideas that can create business value.  

Automating repetitive manual tasks using DevOps automation tools drives notable efficiencies and productivity boosts for developers and organizations, using automatic actions that eliminate frequent developer or operations team intervention. 

What DevOps processes can you automate?

DevOps automation is especially valuable because it can be used on a broad spectrum of tasks in the application development environment, including CI/CD pipelines and workflows, code writing, monitoring and logging, and Infrastructure as Code (IaC) tools. It can also help improve and streamline configuration management, infrastructure provisioning, unit tests, code testing, security steps and scans, troubleshooting, code review, deploying and delivering code, project management, and more.

By bringing beneficial and time-saving automation to the DevOps lifecycle, developers can create cleaner and more secure code with much less manual intervention and human error compared to traditional software development methods. 

Benefits of DevOps automation tools

For development and operations teams, using DevOps automation to streamline and improve their operations goes far beyond just reducing human error rates and increasing the efficiency and speed of code creation and the deployment process.

Other benefits of DevOps automation include improved consistency and reliability, delivery of predictable and repeatable results, and enhanced scalability and manageability of multiple applications and processes. These benefits become possible with automation because it reduces many human mistakes and miscalculations.

DevOps automation benefits can also include smoother collaboration among multiple developers working on applications at the same time by automatically handling merge conflicts, and performing automatic code testing for multiple developers at once. Automation that troubleshoots applications can also speed up project development times by immediately notifying systems personnel of problems as they arise.

How to automate DevOps with Docker

As a flexible tool for DevOps automation, Docker is available in four subscription levels, from the free Docker Personal version to the top-of-the-line Docker Business tier. 

Docker Business delivers a wide range of helpful tools that empower DevOps teams to identify development bottlenecks where automation can free up resources and resolve repetitive tasks and operations. The following tools are included with Docker Business. (Read our September 2024 announcement about upgraded Docker subscription plans that will deliver even more value, flexibility, and power to your development workflows.) 

Docker Image Access Management

With Docker Business, developers and operations teams can quickly start automating tasks using features such as Docker Image Access Management, which gives administrators control over the types of container images that developers can pull and use from Docker Hub. This includes Docker Official Images, Docker Verified Publisher Images, and community images. Using Image Access Management, developers and teams can more easily search private registries and community repositories for needed container images to use to build their applications. 

Image Access Management allows organizations to give developers freedom of choice while providing some guardrails to prevent developers from accidentally using untrusted, malicious community images as components of their applications. This is an important benefit, compared with only allowing developers to use a handful of internally built images, for example.

Docker Image Access Management is available only to Docker Business customers.  

Docker automated testing 

Other Docker DevOps automation features include automated testing, including source code repository testing, that can be done through Docker Hub to automatically test changes to source code repositories using containers. Any Docker Hub repository can enable an autotest function to run tests on pull requests to the source code repository to create a continuous integration testing service.

Automated test files to perform the tests can be set up by creating a docker-compose.test.yml file, which defines a service that lists the tests to be run. The docker-compose.test.yml file should be placed in the same directory that contains the Dockerfile used to build the image.

Hardened Docker Desktop

To automate security within Docker, administrators can use a wide range of features within Hardened Docker Desktop, which is available to Docker Business subscribers. Hardened Docker Desktop security features aim to bolster the security of developer environments while causing minimal speed or performance impacts on developer experiences or productivity. 

These features allow administrators to enforce strict security settings, which prevent developers and containers from bypassing the controls intentionally or unintentionally. The features also enable enhanced container isolation capabilities to prevent potential security threats, such as malicious payloads, from breaching the Docker Desktop Linux VM and the underlying host.

Using Hardened Docker Desktop, security administrators can take more control and ownership over Docker Desktop configurations, removing and preventing potential changes by users, which is vital for security-conscious organizations.

Automated builds

Another automation and productivity tool is the Docker Automated builds feature, which automatically builds images from source code in an external repository and then pushes the built image to designated Docker repositories. Available in the Docker Business, Pro, or Teams tiers, Automated builds — also called autobuilds — create a list of branches and tags that can be built into Docker images using a series of commands. Automated builds can handle images of up to 10 GB in size.

Enhanced collaboration tools 

Throughout Docker’s unified suite, tools built to deliver enhanced collaboration are available to developers and operations teams to work together to get the most out of their projects and applications.

Everything from Docker Desktop to Docker Engine, Docker CLI, Docker Compose, Docker Build/BuildKit, Docker Desktop Extensions, and more are designed to enable developers and operations teams to accelerate productivity, reduce code errors, increase security, drive innovation, and save valuable time throughout the software development process. 

Easier scaling and orchestration with Kubernetes integration

Docker’s containerization platform also integrates well with the Kubernetes container orchestration platform, optimizing the developer experience for container development, deployment, and management. Docker and Kubernetes can work together using Docker Engine as a user-friendly and secure foundation for basic Kubernetes (K8s) functionality, or by using Docker Desktop for a more comprehensive approach that avoids potential challenges associated with do-it-yourself container configurations. Docker Desktop includes K8s setup at the push of a button, which is one of its numerous and useful automation features. 

Support and troubleshooting 

As Docker continues to mature, its knowledge base is constantly being expanded and deepened, with core documentation and resources freely available to Docker developers within the Docker ecosystem. And, because Docker uses a collaborative approach between developers and operations teams, developers can often find common answers to their inquiries and learn from each other to tackle most issues.

More information and help about using Docker can be found in the Docker Training page, which offers live and on-demand training and other resources to help developers and teams negotiate their Docker landscapes and learn fresh skills to resolve technical problems. 

Other resources: Docker Scout and Docker Build Cloud

Docker offers even more tools to help with automation, collaboration, and creating better and more nimble code for developer teams and operations managers.

Docker Scout, for example, is built to help organizations better protect their software supply chain security when using container images, which may contain software elements that are susceptible to security vulnerabilities. 

Docker Scout helps with this issue by proactively analyzing container images and compiling a Software Bill of Materials (SBOM), which is a detailed inventory of code included in an application or container. That SBOM is then matched against a continuously updated vulnerability database to pinpoint and correct security weaknesses to help make the code more secure.

Docker Build Cloud is a Docker service to help developers build container images more quickly, both locally and in the cloud. Those builds run on cloud infrastructure that requires no configuration and where the environment is optimally dimensioned for all workloads using a remote build cache. This approach ensures fast builds anywhere for all team members. 

To use Docker Build Cloud, developers take the same steps they would take for a regular build using the command docker buildx build. With a regular build command, the build runs on a local instance of BuildKit, bundled with the Docker daemon. But when using Docker Build Cloud, the build request is sent to a BuildKit instance running remotely, in the cloud, with all data encrypted in transit. Docker Build Cloud provides several benefits over local builds, including faster build speed, shared build cache, and native multi-platform builds.

Future trends in DevOps automation

As DevOps automation continues to mature, it will gain more capabilities from artificial intelligence (AI), machine learning (ML), serverless architectures, cloud-native platforms, and other technologies across the IT landscape. 

Such advancements can be found in Docker’s AI collaborations with NVIDIA. For example, Docker Desktop dovetails with the NVIDIA AI Workbench, which is an easy-to-use toolkit that lets developers create, test, and customize AI and machine learning models on a PC or workstation and then scale them to a data center or public cloud. NVIDIA AI Workbench makes interactive development workflows easier, while automating technical tasks that can halt beginners and derail experts. 

DevOps automation is ripe for further improvements and enhancements from AI and ML in areas of agility, process improvements, and more for developers and operations teams. AI and ML will drive further labor savings for software development teams by delivering fresh new automated, self-service tools that free them up from a broader range of routine tasks, giving them more time to conduct valuable and critical work that will drive their companies forward.

Docker will be an important part of this changing landscape as the unified suites and tools continue to expand and deliver further new benefits and capabilities to DevOps, the Docker ecosystem, and developers and operations teams around the world.

Wrapping up

Improving DevOps automation by using the Docker containerization platform inside your business organization is a smart strategy that helps developers and operations teams deliver their best work with efficiency, creativity, and broad collaboration.

Docker Business plays a leadership role in enhancing DevOps automation in companies around the world as they look to automate their DevOps operations effectively.

Ready to automate your team’s DevOps processes? Find out how Docker Business can transform your development, or if you still have questions, reach out to one of our experts to get started!

Learn more

Subscribe to the Docker Newsletter. 

Learn about the 2024 announcement of upgraded Docker plans that are simpler and offer even more value. 

Learn how other DevOps teams use Docker. 

Find the perfect pricing for your team. 

Try the latest version of Docker Desktop. 

Visit Docker Resources to explore more materials.

Quelle: https://blog.docker.com/feed/

Leveraging Testcontainers for Complex Integration Testing in Mattermost Plugins

This post was contributed by Jesús Espino, Principal Engineer at Mattermost.

In the ever-evolving software development landscape, ensuring robust and reliable plugin integration is no small feat. For Mattermost, relying solely on mocks for plugin testing became a limitation, leading to brittle tests and overlooked integration issues. Enter Testcontainers, an open source tool that provides isolated Docker environments, making complex integration testing not only feasible but efficient. 

In this blog post, we dive into how Mattermost has embraced Testcontainers to overhaul its testing strategy, achieving greater automation, improved accuracy, and seamless plugin integration with minimal overhead.

The previous approach

In the past, Mattermost relied heavily on mocks to test plugins. While this approach had its merits, it also had significant drawbacks. The tests were brittle, meaning they would often break when changes were made to the codebase. This made the tests challenging to develop and maintain, as developers had to constantly update the mocks to reflect the changes in the code.

Furthermore, the use of mocks meant that the integration aspect of testing was largely overlooked. The tests did not account for how the different components of the system interacted with each other, which could lead to unforeseen issues in the production environment. 

The previous approach additionally did not allow for proper integration testing in an automated way. The lack of automation made the testing process time-consuming and prone to human error. These challenges necessitated a shift in Mattermost’s testing strategy, leading to the adoption of Testcontainers for complex integration testing.

Mattermost’s approach to integration testing

Testcontainers for Go

Mattermost uses Testcontainers for Go to create an isolated testing environment for our plugins. This testing environment includes the Mattermost server, the PostgreSQL server, and, in certain cases, an API mock server. The plugin is then installed on the Mattermost server, and through regular API calls or end-to-end testing frameworks like Playwright, we perform the required testing.

We have created a specialized Testcontainers module for the Mattermost server. This module uses PostgreSQL as a dependency, ensuring that the testing environment closely mirrors the production environment. Our module allows the developer to install and configure any plugin you want in the Mattermost server easily.

To improve the system’s isolation, the Mattermost module includes a container for the server and a container for the PostgreSQL database, which are connected through an internal Docker network.

Additionally, the Mattermost module exposes utility functionality that allows direct access to the database, to the Mattermost API through the Go client, and some utility functions that enable admins to create users, channels, teams, and change the configuration, among other things. This functionality is invaluable for performing complex operations during testing, including API calls, users/teams/channel creation, configuration changes, or even SQL query execution. 

This approach provides a powerful set of tools with which to set up our tests and prepare everything for verifying the behavior that we expect. Combined with the disposable nature of the test container instances, this makes the system easy to understand while remaining isolated.

This comprehensive approach to testing ensures that all aspects of the Mattermost server and its plugins are thoroughly tested, thereby increasing their reliability and functionality. But, let’s see a code example of the usage.

We can start setting up our Mattermost environment with a plugin like this:

pluginConfig := map[string]any{}
options := []mmcontainer.MattermostCustomizeRequestOption{
mmcontainer.WithPlugin("sample.tar.gz", "sample", pluginConfig),
}
mattermost, err := mmcontainer.RunContainer(context.Background(), options…)
defer mattermost.Terminate(context.Background()

Once your Mattermost instance is initialized, you can create a test like this:

func TestSample(t *testing.T) {
client, err mattermost.GetClient()
require.NoError(t, err)
reqURL := client.URL + "/plugins/sample/sample-endpoint"
resp, err := client.DoAPIRequest(context.Background(), http.MethodGet, reqURL, "", "")
require.NoError(t, err, "cannot fetch url %s", reqURL)
defer resp.Body.Close()
bodyBytes, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, 200, resp.StatusCode)
assert.Contains(t, string(bodyBytes), "sample-response")
}

Here, you can decide when you tear down your Mattermost instance and recreate it. Once per test? Once per a set of tests? It is up to you and depends strictly on your needs and the nature of your tests.

Testcontainers for Node.js

In addition to using Testcontainers for Go, Mattermost leverages Testcontainers for Node.js to set up our testing environment. In case you’re unfamiliar, Testcontainers for Node.js is a Node.js library that provides similar functionality to Testcontainers for Go. Using Testcontainers for Node.js, we can set up our environment in the same way we did with Testcontainers for Go. This allows us to write Playwright tests using JavaScript and run them in the isolated Mattermost environment created by Testcontainers, enabling us to perform integration testing that interacts directly with the plugin user interface. The code is available on GitHub.  

This approach provides the same advantages as Testcontainers for Go, and it allows us to use a more interface-based testing tool — like Playwright in this case. Let me show a bit of code with the Node.js and Playwright implementation:

We start and stop the containers for each test:

test.beforeAll(async () => { mattermost = await RunContainer() })
test.afterAll(async () => { await mattermost.stop(); })

Then we can use our Mattermost instance like any other server running to run our Playwright tests:

test.describe('sample slash command', () => {
test('try to run a sample slash command', async ({ page }) => {
const url = mattermost.url()
await login(page, url, "regularuser", "regularuser")
await expect(page.getByLabel('town square public channel')).toBeVisible();
await page.getByTestId('post_textbox').fill("/sample run")
await page.getByTestId('SendMessageButton').click();
await expect(page.getByText('Sample command result', { exact: true })).toBeVisible();
await logout(page)
});
});

With these two approaches, we can create integration tests covering the API and the interface without having to mock or use any other synthetic environment. Also, we can test things in absolute isolation because we consciously decide whether we want to reuse the Testcontainers instances. We can also reach a high degree of isolation and thereby avoid the flakiness induced by contaminated environments when doing end-to-end testing.

Examples of usage

Currently, we are using this approach for two plugins.

1. Mattermost AI Copilot

This integration helps users in their daily tasks using AI large language models (LLMs), providing things like thread and meeting summarization and context-based interrogation.

This plugin has a rich interface, so we used the Testcontainers for Node and Playwright approach to ensure we could properly test the system through the interface. Also, this plugin needs to call the AI LLM through an API. To avoid that resource-heavy task, we use an API mock, another container that simulates any API.

This approach gives us confidence in the server-side code but in the interface side as well, because we can ensure that we aren’t breaking anything during the development.

2. Mattermost MS Teams plugin

This integration is designed to connect MS Teams and Mattermost in a seamless way, synchronizing messages between both platforms.

For this plugin, we mainly need to do API calls, so we used Testcontainers for Go and directly hit the API using a client written in Go. In this case, again, our plugin depends on a third-party service: the Microsoft Graph API from Microsoft. For that, we also use an API mock, enabling us to test the whole plugin without depending on the third-party service.

We still have some integration tests with the real Teams API using the same Testcontainers infrastructure to ensure that we are properly handling the Microsoft Graph calls.

Benefits of using Testcontainers libraries

Using Testcontainers for integration testing offers benefits, such as:

Isolation: Each test runs in its own Docker container, which means that tests are completely isolated from each other. This approach prevents tests from interfering with one another and ensures that each test starts with a clean slate.

Repeatability: Because the testing environment is set up automatically, the tests are highly repeatable. This means that developers can run the tests multiple times and get the same results, which increases the reliability of the tests.

Ease of use: Testcontainers is easy to use, as it handles all the complexities of setting up and tearing down Docker containers. This allows developers to focus on writing tests rather than managing the testing environment.

Testing made easy with Testcontainers

Mattermost’s use of Testcontainers libraries for complex integration testing in their plugins is a testament to the power and versatility of Testcontainers.

By creating a well-isolated and repeatable testing environment, Mattermost ensures that our plugins are thoroughly tested and highly reliable.

Learn more

Subscribe to the Docker Newsletter. 

Visit the Testcontainers website.

Get started with Testcontainers Cloud by creating a free account.

Vote on what’s next! Check out our public roadmap.

Have questions? The Docker community is here to help.

New to Docker? Get started.

Quelle: https://blog.docker.com/feed/