You are on page 1of 68

Kubernetes Programming with Go:

Programming Kubernetes Clients and


Operators Using Go and the
Kubernetes API 1st Edition Philippe
Martin
Visit to download the full and correct content document:
https://ebookmass.com/product/kubernetes-programming-with-go-programming-kuber
netes-clients-and-operators-using-go-and-the-kubernetes-api-1st-edition-philippe-mar
tin-2/
Philippe Martin

Kubernetes Programming with Go


Programming Kubernetes Clients and Operators
Using Go and the Kubernetes API
Philippe Martin
Blanquefort, France

ISBN 978-1-4842-9025-5 e-ISBN 978-1-4842-9026-2


https://doi.org/10.1007/978-1-4842-9026-2

© Philippe Martin 2023

This work is subject to copyright. All rights are solely and exclusively
licensed by the Publisher, whether the whole or part of the material is
concerned, specifically the rights of translation, reprinting, reuse of
illustrations, recitation, broadcasting, reproduction on microfilms or in
any other physical way, and transmission or information storage and
retrieval, electronic adaptation, computer software, or by similar or
dissimilar methodology now known or hereafter developed.

The use of general descriptive names, registered names, trademarks,


service marks, etc. in this publication does not imply, even in the
absence of a specific statement, that such names are exempt from the
relevant protective laws and regulations and therefore free for general
use.

The publisher, the authors, and the editors are safe to assume that the
advice and information in this book are believed to be true and accurate
at the date of publication. Neither the publisher nor the authors or the
editors give a warranty, expressed or implied, with respect to the
material contained herein or for any errors or omissions that may have
been made. The publisher remains neutral with regard to jurisdictional
claims in published maps and institutional affiliations.

This Apress imprint is published by the registered company APress


Media, LLC, part of Springer Nature.
The registered company address is: 1 New York Plaza, New York, NY
10004, U.S.A.
To Mélina and Elsa, my constant source of truth
Introduction
Back in 2017, I was working for a company building video streaming
software. At the end of that year, a small team, including me, got
assigned a new job to work on deploying the Video CDN developed by
the company on Kubernetes. We decided to explore the concept of
Custom Resources and Operators to deploy this CDN.
The current Kubernetes release was 1.9, the concept of Custom
Resource Definition had just been released in 1.7, and the sample-
controller repository was the only documentation we knew of to help
build an Operator. The Kubernetes ecosystem, being especially lively,
had tools appearing in the following months, specifically the
Kubebuilder SDK. Thus, our project was launched.
From that moment on, I spent numerous days exploring how to
build Operators and other programs interacting with the Kubernetes
API. But the damage was done: I had started to learn Kubernetes
programming from specific to general, and it took me a long time to
fully understand the innards of the Kubernetes API.
I have written this book in the hope that it can teach new
Kubernetes developers how to program, from general to specific, with
the Kubernetes API in Go.

Chapters at a Glance
The target reader for this book has some experience working with
REST APIs, accessing them either by HTTP or using clients for specific
languages; and has some knowledge of the Kubernetes platform,
essentially as a user—for example, some experience deploying such
APIs or frontend applications with the help of YAML manifests.
Chapter 1 of the book explores the Kubernetes API and how it
implements the principles of REST. It especially focuses on the
Group-Version-Resource organization and the Kind concept
proposed by the API.
Chapter 2 continues by covering the operations proposed by the API
and the details of each operation, using the HTTP protocol.
Chapters 3 to 5 describe the common and “low-level” Go libraries to
work with the Kubernetes API: the API and API Machinery Libraries.
Chapters 6 and 7 cover the Client-go Library—the high-level library
to work with the Kubernetes API in Go—and how to unit test code
using this library.
At this point in the book, the reader should be comfortable with
building Go applications working with native resources of the
Kubernetes API.
Chapters 8 and 9 introduce the concept of Custom Resources and
how to work with them in Go.
Chapters 10 to 12 cover the implementation of Kubernetes Operators
using the controller-runtime library.
Chapter 13 explores the Kubebuilder SDK, a tool to help develop and
deploy Kubernetes Operators.
By the end of the book, the reader should be able to start building
Kubernetes operators in Go and have a very good understanding of
what happens behind the scenes.
Any source code or other supplementary material referenced by the
author in this book is available to readers on GitHub via the book's
product page, located at https://github.com/Apress/Kubernetes-
Programming-with-Go-by-Philippe-Martin. For more detailed
information, please visit http://www.apress.com/source-code.
Acknowledgments
I would like to thank the whole Anevia “CDN” team who started
working with me on Kubernetes back in 2018: David, Ansou, Hossam,
Yassine, É tienne, Jason, and Michaël. Special thanks to Damien Lucas for
initiating this project and for having trusted us with this challenge.
My discovery of Kubernetes has been much easier and pleasant
thanks to the TGIK channel and its numerous episodes, hosted by Joe
Beda, Kris Nova, and many others. Plus, thanks to all the Kubernetes
community for such a great ecosystem!
Table of Contents
Chapter 1:​Kubernetes API Introduction
Kubernetes Platform at a Glance
OpenAPI Specification
Verbs and Kinds
Group-Version-Resource
Sub-resources
Official API Reference Documentation
The Deployment Documentation
Operations Documentation
The Pod Documentation
One-Page Version of the Documentation
Conclusion
Chapter 2:​Kubernetes API Operations
Examining Requests
Making Requests
Using kubectl as a Proxy
Creating a Resource
Getting Information About a Resource
Getting the List of Resources
Filtering the Result of a List
Deleting a Resource
Deleting a Collection of Resources
Updating a Resource
Managing Conflicts When Updating a Resource
Using a Strategic Merge Patch to Update a Resource
Applying Resources Server-side
Watching Resources
Filtering During a Watch Session
Watching After Listing Resources
Restarting a watch Request
Allowing Bookmarks to Efficiently Restart a watch Request
Paginating Results
Getting Results in Various Formats
Getting Results as a Table
Using the YAML Format
Using the Protobuf Format
Conclusion
Chapter 3:​Working with API Resources in Go
API Library Sources and Import
Content of a Package
types.​go
register.​go
doc.​go
generated.​pb.​go and generated.​proto
types_​swagger_​doc_​generated.​go
zz_​generated.​deepcopy.​go
Specific Content in core/​v1
ObjectReference
ResourceList
Taint
Toleration
Well-Known Labels
Writing Kubernetes Resources in Go
Importing the Package
The TypeMeta Fields
The ObjectMeta Fields
Spec and Status
Comparison with Writing YAML Manifests
A Complete Example
Conclusion
Chapter 4:​Using Common Types
Pointers
Getting the Reference of a Value
Dereferencing a Pointer
Comparing Two Referenced Values
Quantities
Parsing a String as Quantity
Using an inf.​Dec as a Quantity
Using a Scaled Integer as a Quantity
Operations on Quantities
IntOrString
Time
Factory Methods
Operations on Time
Conclusion
Chapter 5:​The API Machinery
The Schema Package
Scheme
Initialization
Mapping
Conversion
Serialization
RESTMapper
Kind to Resource
Resource to Kind
Finding Resources
The DefaultRESTMappe​r Implementation
Conclusion
Chapter 6:​The Client-go Library
Connecting to the Cluster
In-cluster Configuration
Out-of-Cluster Configuration
Getting a Clientset
Using the Clientset
Examining the Requests
Creating a Resource
Getting Information About a Resource
Getting List of Resources
Filtering the Result of a List
Setting LabelSelector Using the Labels Package
Setting Fieldselector Using the Fields Package
Deleting a Resource
Deleting a Collection of Resources
Updating a Resource
Using a Strategic Merge Patch to Update a Resource
Applying Resources Server-side with Patch
Server-side Apply Using Apply Configurations
Building an ApplyConfigurati​on from Scratch
Building an ApplyConfigurati​on from an Existing Resource
Watching Resources
Errors and Statuses
Definition of the metav1.​Status Structure
Error Returned by Clientset Operations
RESTClient
Building the Request
Executing the Request
Exploiting the Result
Getting Result as a Table
Discovery Client
RESTMapper
PriorityRESTMapp​er
DeferredDiscover​yRESTMapper
Conclusion
Chapter 7:​Testing Applications Using Client-go
Fake Clientset
Checking the Result of the Function
Reacting to Actions
Checking the Actions
Fake REST Client
FakeDiscovery Client
Stubbing the ServerVersion
Actions
Mocking Resources
Conclusion
Chapter 8:​Extending Kubernetes API with Custom Resources
Definitions
Performing Operations in Go
The CustomResourceDe​finition in Detail
Naming the Resource
Definition of the Resource Versions
Converting Between Versions
Schema of the Resource
Deploying a Custom Resource Definition
Additional Printer Columns
Conclusion
Chapter 9:​Working with Custom Resources
Generating a Clientset
Using deepcopy-gen
Using client-gen
Using the Generated Clientset
Using the Generated fake Clientset
Using the Unstructured Package and Dynamic Client
The Unstructured Type
The UnstructuredList​Type
Converting Between Typed and Unstructured Objects
The Dynamic Client
Conclusion
Chapter 10:​Writing Operators withthe Controller-Runtime
Library
The Manager
The Controller
Creating a Controller
Watching Resources
A First Example
Using the Controller Builder
A Second Example Using the ControllerBuilde​r
Injecting Manager Resources into the Reconciler
Using the Client
Getting Information About a Resource
Listing Resources
Creating a Resource
Deleting a Resource
Deleting a Collection of Resources
Updating a Resource
Patching a Resource
Updating the Status of a Resource
Patching the Status of a Resource
Logging
Verbosity
Predefined Values
Logger Name
Getting the Logger from Context
Events
Conclusion
Chapter 11:​Writing the Reconcile Loop
Writing the Reconcile Function
Checking Whether the Resource Exists
Implementing the Reconciled Resource
Simple Implementation Example
Conclusion
Chapter 12:​Testing the Reconcile Loop
The envtest Package
Installing envtest Binaries
Using envtest
Defining a ginkgo Suite
Writing the Tests
Test 1
Test 2
Test 3
Test 4
Conclusion
Chapter 13:​Creating an Operator with Kubebuilder
Installing Kubebuilder
Creating a Project
Adding a Custom Resource to the Project
Building and Deploying Manifests
Running the Manager Locally
Personalizing the Custom Resource
Editing the Go Structures
Enabling the Status Subresource
Defining Printer Columns
Regenerating the Files
Implementing the Reconcile Function
Adding RBAC Annotations
Deploying the Operator on the Cluster
Creating a New Version of the Resource
Defining a New Version
Implementing Hub and Convertible
Setting Up the webhook
Updating kustomization Files
Using Various Versions
Conclusion
Index
About the Author
Philippe Martin
has been working with Kubernetes for
five years, first by creating an Operator
to deploy video CDNs into the cloud,
later helping companies deploy their
applications into Kubernetes, then
writing a Client to help developers work
in a Kubernetes environment. Philippe
has passed the CKAD, CKA, and CKS
certifications. He has extensive
experience with distributed systems and
open-source software: he started his
career 20 years ago creating thin clients
based on the Linux kernel and open-
source components. He is currently
working at Red Hat on the Development
Tools team.
Philippe has been active in the development of Kubernetes,
especially its documentation, and participates in the translation of the
official documentation into French, has edited two reference books
about the Kubernetes API and kubectl, and is responsible for the
French translation of the Kubernetes Dashboard. He participated in
Google Season of Docs to create the new Kubernetes API Reference
section of the official documentation and is maintaining it.
About the Technical Reviewers
Bartosz Majsak
writes code for fun and profit while
proudly wearing a red fedora (also
known as the Red Hat). He has been
long-time open-source contributor and
Java developer turned into Golang
aficionado. Bartosz is overly enthusiastic
about coffee, open source, and speaking
at conferences, not necessarily in that
order. One thing that perhaps proves he
is not a total geek is his addiction to
alpine skiing (and running).

Prasanth
is a Blockchain Certified Professional,
Professional Scrum Master, and
Microsoft Certified Trainer who is
passionate about helping others learn
how to use and gain benefits from the
latest technologies. He is a thought
leader and practitioner in Blockchain,
Cloud, and Scrum. He also handles the
Agile Methodology, Cloud, and
Blockchain technology community
initiatives within TransUnion through
coaching, mentoring, and grooming
techniques.
Prasanth is an adjunct professor and
a technical speaker. He was selected as a speaker at the China
International Industry Big Data Expo 2018 by the Chinese government
and also was invited to the International Blockchain Council by the
Government of Telangana and Goa. In addition, he received accolades
from the Chinese government for his presentation at China
International Industry Big Data Expo 2018. Prasanth has published his
Patent, entitled “Digital Educational Certificate Management System
Using IPFS-Based Blockchain.”
To date, he has interacted extensively, reaching more than 50,000
students, mostly within the technical domain. Prasanth is a working
group member of the CryptoCurrency Certification Consortium, the
Scrum Alliance, the Scrum Organization, and the International Institute
of Business Analysis.
© The Author(s), under exclusive license to APress Media, LLC, part of Springer
Nature 2023
P. Martin, Kubernetes Programming with Go
https://doi.org/10.1007/978-1-4842-9026-2_1

1. Kubernetes API Introduction


Philippe Martin1

(1) Blanquefort, France

Kubernetes is a platform to orchestrate containers operating in the


declarative mode. There are one-thousand-and-one ways to describe
how the Kubernetes platform is constructed. This book focuses on
programming with the platform.
The entry point of the Kubernetes platform is the API. This chapter
explores the Kubernetes architecture by highlighting the central role of
the Kubernetes API. It then focuses on the HTTP REST nature of the
Kubernetes API, and on the extensions added to organize the many
resources managed by it.
Finally, you will learn how to navigate the reference documentation
effectively to be able to extract the maximum quantity of useful
information daily.

Kubernetes Platform at a Glance


On one side of the chain, the user declares the high-level resources to
build applications to deploy: Deployments, Ingresses, and so on.
In the middle, controllers are activated to transform these resources
into low-level resources (Pods), and the scheduler distributes these
resources into nodes. On the other side of the chain, the node agents
deploy the low-level resources onto nodes.
The main elements of the Kubernetes platform (commonly called
the control-plane) are highlighted in Figure 1-1 and described in the
following:
1. The API server – this is the central point on the control-plane; the
user and the various pieces of the control-plane contact this API to
create, get, delete, update, and watch resources.

2. The etcd database – this is only accessible by the API Server, is used
to persist the data relative to resources.

3. The Controller manager – this runs the controllers that transform


high-level resources declared by the user into low-level resources
to be deployed on nodes. The controllers are connected to the API
Server, watching for high-level resources and creating, deleting, and
updating low-level resources to satisfy the specifications declared
in high-level resources.

4. Scheduler – this distributes the low-level resources on the various


nodes. The Scheduler is connected to the API Server to watch for
unaffected resources and connect them to nodes.

5. Kubelet – this is an agent running on all nodes of the cluster, and


each agent manages the workloads affected to its node. The kubelet
is connected to the API Server to watch for Pods resources affected
to its node and to deploy the associated containers using the local
container runtime.

6. Kube proxy – this is an agent running on all nodes of the cluster,


and each agent manages the network configurations affected to its
node. The kube proxy is connected to the API Server to watch for
Service resources and to configure associated network rules on its
node.
Figure 1-1 The architecture of Kubernetes

OpenAPI Specification
The Kubernetes API is an HTTP REST API. The Kubernetes team
provides a specification for this API in the OpenAPI format, either in v2
format at
https://github.com/kubernetes/kubernetes/tree/mast
er/api/openapi-spec or in Kubernetes v1.24, in v3 format, at
https://github.com/kubernetes/kubernetes/tree/mast
er/api/openapi-spec/v3.
These specifications also are accessible from the API Server at these
paths: /openapi/v2 and /openapi/v3.
An OpenAPI specification is made up of various parts and, among
these, are a list of paths and a list of definitions. The paths are the URLs
you use to request this API, and for each path, the specification gives
the distinct operations such as get, delete, or post. Then for each
operation, the specification indicates what are the parameters and body
format for the request, and what are the possible response codes and
associated body format for the response.
The parameters and bodies for requests and responses can be
either simple types or, more generally, structures containing data. The
list of definitions includes data structures that help build the
parameters and bodies for the operations’ requests and responses.
Figure 1-2 is a simplified view of a specification for a User API. This
API can accept two different paths: /user/{userId} and /user. The first
path, /user/{userId}, can accept two operations, get and delete,
respectively, to receive information about a specific user, given its user
ID; and to delete information about a specific user, given its user ID. The
second path, /user, can accept a single operation, post, to add a new
user, given its information.
In this API, a definition of a structure User is given, describing the
information for a user: its ID, first name, and last name. This data
structure is used in the response body of the get operation on the first
path, and in the request body of the post operation on the second path.
Figure 1-2 A simplified user API specification

Verbs and Kinds


The Kubernetes API adds two concepts to this specification: the
Kubernetes API Verbs and the Kubernetes Kinds.
The Kubernetes API Verbs are mapped directly to the operations in
the OpenAPI specification. The defined verbs are get, create, update,
patch, delete, list, watch, and deletecollection. The correspondence
with the HTTP verbs can be found in Table 1-1.
Table 1-1 Correspondence Between Kubernetes API Verbs and HTTP Verbs

Kubernetes API Verb HTTP Verb


get GET
create POST
update PUT
patch PATCH
delete DELETE
list GET
watch GET
deletecollection DELETE
The Kubernetes Kinds are a subset of the definitions in the OpenAPI
specification. When requests are made to the Kubernetes API, data
structures are exchanged through the bodies of requests and responses.
These structures share common fields, apiVersion and kind, to help
the participants of the request recognize these structures.
If you wanted to make your User API manage this Kind concept, the
User structure would contain two additional fields, apiVersion and
kind—for example, with values v1 and User. To determine whether a
definition in the Kubernetes OpenAPI specification is a Kubernetes
Kind, you can look at the x-kubernetes-group-version-kind field of
the definition. If this field is defined, the definition is a kind, and it gives
you the values of the apiVersion and kind fields.

Group-Version-Resource
The Kubernetes API is a REST API, and as a result of that it manages
Resources, and the paths to manage these resources follow the REST
naming conventions—that is, by using a plural name to identify a
resource and by grouping these resources.
Because the Kubernetes API manages hundreds of resources, they
are grouped together, and because the API evolves, the resources are
versioned. For these reasons, each resource belongs to a given Group
and Version, and each resource is uniquely identified by a Group-
Version-Resource, commonly known as GVR.
To find the various resources in the Kubernetes API, you can browse
the OpenAPI specification to extract the distinct paths. Legacy
resources (e.g., pods or nodes) will have been introduced early in the
Kubernetes API and all belong to the group core and the version v1.
The paths to manage legacy resources cluster-wide follow the
format /api/v1/<plural_resource_name>—for example,
/api/v1/nodes to manage nodes. Note that the core group is not
represented in the path. To manage resources in a given namespace, the
path format is
/api/v1/namespaces/<namespace_name>/<plural_resource_nam
e>—for example, /api/v1/namespaces/default/pods to manage
pods in the default namespace.
Newer resources are accessible through paths following the format
/apis/<group>/<version>/<plural_resource_name> or
/apis/<group>/<version>/namespaces/<namespace_name>/<plu
ral_resource_name>.
To summarize, the formats of the various paths to access resources
are:
/api/v1/<plural_name> – to access legacy non-namespaced
resources
Ex: /api/v1/nodes to access non-namespaced nodes resources
or
To access legacy namespaced resources cluster-wide
Ex: /api/v1/pods to access pods across all namespaces
/api/v1/namespaces/<ns>/<plural_name> – to access legacy
namespaced resources in a specific namespace
Ex: /api/v1/namespaces/default/pods to access pods in the
default namespace
/apis/<group>/<version>/<plural_name> – to access non-
namespaced resources in specific group and version
Ex: /apis/storage.k8s.io/v1/storageclasses to access non-
namespaced storageclasses (group storage.k8s.io, version v1)
or
To access namespaced resources cluster-wide
Ex: /apis/apps/v1/deployments to access deployments across
all namespaces
/apis/<group>/<version>/namespaces/<ns>/<plural_name> – to
access namespaced resources in a specific namespace
Ex: /apis/apps/v1/namespaces/default/deployments to
access deployments (group apps, version v1) in the default
namespace

Sub-resources
Following the REST API convention, the resources can have sub-
resources. A sub-resource is a resource that belongs to another and can
be accessed by specifying its name after the name of the resource, as
follows:
/api/v1/<plural>/<res-name>/<sub-resource>
Ex: /api/v1/nodes/node1/status
/api/v1/namespaces/<ns>/<plural>/<res-name>/<sub-resource>
Ex: /api/v1/namespaces/ns1/pods/pod1/status
/apis/<group>/<version>/<plural>/<res-name>/<sub-resource>
Ex: /apis/storage.k8s.io/v1/volumeattachments/volatt1/status
/apis/<grp>/<v>/namespaces/<ns>/<plural>/<name>/<sub-res>
Ex: /apis/apps/v1/namespaces/ns1/deployments/dep1/status
Most Kubernetes resources have a status sub-resource. You can see,
when writing operators, that the operator needs to update the status
sub-resource to be able to indicate the state of this resource observed
by the operator. The operations that can be executed in the status sub-
resource are get, patch, and update. The Pod has more sub-resources,
including attach, binding, eviction, exec, log, portforward, and
proxy. These sub-resources are useful for getting information about a
specific running pod, or executing some specific operation on a running
pod, and so on.
The resources that can Scale (i.e., deployments, replicasets, etc.)
have a scale sub-resource. The operations that can be executed in the
scale sub-resource are get, patch, and update.

Official API Reference Documentation


The official reference documentation of the API can be found at
https://kubernetes.io/docs/reference/kubernetes-
api/. The resources managed by the API are first grouped together by
category (i.e., workloads, storage, etc.), and for each category, you can
obtain a list of resource names with a short description (Figure 1-3).
Note that these categories are not part of the Kubernetes API
definition but are used in this website to help inexperienced users find
their way into the multitude of available resources.

Figure 1-3 The Kubernetes resources grouped by category

To be precise, the name displayed is not the resource name in the


REST sense, but the associated principal kind, as shown in Figure 1-4.
For example, when managing Pods, the resource name used in the REST
paths is pods (i.e., lowercase and plural), and the definition used to
exchange information about Pods during HTTP requests is named Pod
(i.e., uppercase and singular). Note that other kinds can be associated
with the same resource. In the example in this chapter, the PodList kind
(used to exchange information about Lists of Pods) also exists.

Figure 1-4 The resources for a specific category, with a short description

The Deployment Documentation


Let’s explore the reference page for the Deployment available at this
address:
https://kubernetes.io/docs/reference/kubernetes-
api/workload-resources/deployment-v1/. The title of the
page, Deployment, is the principal kind associated with the
deployments resource shown in Figure 1-5.
Figure 1-5 The Deployment documentation page
The apiVersion indicated in the header can help you write a YAML
manifest for a Deployment resource because you need to specify, for
each resource in a Kubernetes manifest, the apiVersion and kind.
In this case, you know the manifest for a deployment will start with
the following:

apiVersion: apps/v1
kind: Deployment

The next header line indicates the import to use when writing Go
code. In Chapter 3, you will see how to use this import when describing
resources in Go.

After the header, a list of structure definitions is described, also


accessible from the table of contents for the Deployment
documentation page in Figure 1-6. The first one is the principal kind of
the resource, optionally followed by structure definitions that are used
in fields of the first kind.

Figure 1-6 Table of contents for the Deployment documentation page


For example, the Deployment kind contains a spec field, of type
DeploymentSpec, which is described later. Note that DeploymentSpec
is not a structure directly exchanged during HTTP requests, and for
that, it is not a kind and does not contain kind or apiVersion fields.
Following the principal kind, and its associated definitions, other
kinds associated with the resource are displayed. In this case, the
DeploymentList kind.
Operations Documentation
The next subject in the API Documentation for a resource is the list of
possible operations on this resource or its sub-resources, also
accessible from the table of contents page (see Figure 1-6). By
examining the details for the create operation to Create a Deployment,
as shown in Figure 1-7, you can see the HTTP Request verb and path to
use, the parameters to pass during the request, and the possible
responses. The HTTP verb to use for the request is POST and the path
is /apis/apps/v1/namespaces/{namespace}/deployments.
Figure 1-7 Details for a “create” Deployment operation
The {namespace} part of the path indicates a path parameter,
which is to be replaced by the name of the namespace on which you
want to create the deployment. You can specify the query parameters:
dryRun, fieldManager, fieldValidation, and pretty. These parameters
will follow the path with the format path?dryRun=All.
The body of the request must be a Deployment kind. When using
kubectl, you are writing Kubernetes Manifests that contain this body. In
Chapter 3, you will see how to build the body in Go. The possible HTTP
codes for the responses are: 200, 201, 202, and 401; and for the 2xx
codes, the response body will contain a Deployment kind.

The Pod Documentation


Some structures contain many fields. For them, the Kubernetes API
documentation categorizes the fields. An example is the documentation
of the Pod resource.
The documentation page for the Pod resource first contains the
description for the principal kind, Pod, followed by the description of
the PodSpec structure. The PodSpec structure contains about 40
fields. To help you understand the relationships between these fields
and to simplify their exploration, they are arranged into categories. The
PodSpec fields’ categories are the following: Containers, Volumes,
Scheduling, Lifecycle, and so on.
Additionally, for fields containing nested fields, descriptions of them
are generally displayed inline to avoid a back and forth between
structure descriptions. For complex structures, however, the
description is reported subsequently on the page, and a link is present
next to the field name to be able to access it easily.
This is always the case for the Spec and Status structures because
they are very commonly found in almost all the resources. In addition,
this is the case for some structures used in the Pod kind—for example,
Container, EphemeralContainer, LifecycleHandler, NodeAffinity,
and so on.
Some structures used in several resources are placed in the
Common Definitions section, and a link is present next to the field name
to access it easily. In Figure 1-8, you can see the Containers category
inside the description of the PodSpec structure.
Figure 1-8 Extract of the PodSpec structure documentation
You also can see that the fields, containers and initContainers, are
of the same type as Container, which is described later on the page and
is accessible with a link. The imagePullSecrets field is of type
LocalObjectReference, which is described in the Common Definitions
section and also is accessible through a link.

One-Page Version of the Documentation


Another version of the API Reference documentation exists and is
presented on a single page. This version covers all the versions of the
resources served by a Kubernretes version (not just the latest one).
This version (if you want, change the last part of the path to navigate to
another Kubernetes version) can be found at the following URL:
https://kubernetes.io/docs/reference/generated/k
ubernetes-api/v1.24/

Conclusion
In this chapter, you have been able to discover the architecture of the
Kubernetes platform, and that the API Server plays a central role. The
Kubernetes API is an HTTP REST API, and the resources are categorized
into various versioned groups.
Kinds are specific structures used to exchange data between the API
server and the clients. You can browse, using the official Kubernetes
website, the API specifications in a human-readable form to discover
the structure of the various resources and kinds, the different
operations available for each resource and sub-resource, and their
associated verbs.
© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2023
P. Martin, Kubernetes Programming with Go
https://doi.org/10.1007/978-1-4842-9026-2_2

2. Kubernetes API Operations


Philippe Martin1

(1) Blanquefort, France

The previous chapter described that the Kubernetes API follows REST principles and
enables users to manipulate resources.
In this chapter, you will learn how to perform various operations by making HTTP
requests directly. During your daily work, you probably will not have to interact
directly with the HTTP layer, but it is important to understand how the API works at
this level so that you can understand how to use more easily it with a higher-level
library.

Examining Requests
Before starting to write your own HTTP requests, you can examine with kubectl
which requests are used when executing kubectl commands. This can be achieved by
using the verbose flag, -v, with a value greater than or equal to 6. Table 2-1 shows
which information is displayed at each level.
For example, if you want to know the URL that is called when getting pods for all
namespaces, you can use the following command:

$ kubectl get pods --all-namespaces -v6


loader.go:372] Config loaded from
file: /home/user/.kube/config
round_trippers.go:553] GET
https://192.168.1.194:6443/api/v1/pods?limit=500 200 OK in
745 milliseconds

In the output of the command, you can see that the path used is /api/v1/pods.
Or, when getting pods in a specific namespace, you can see that the path used is
/api/v1/namespaces/default/pods:

$ kubectl get pods --namespace default -v6


loader.go:372] Config loaded from
file: /home/user/.kube/config
round_trippers.go:553] GET
https://192.168.1.194:6443/api/v1/namespaces/default/pods?
limit=500 200 OK in 138 milliseconds

Table 2-1 Verbosity Levels

Level Method Request Events Request Response Response Curl Body


and URL timing timing headers status headers cmd length
-v 6 yes yes – – – – – 0
-v 7 yes – – yes yes – – 0
-v 8 yes – – yes yes yes - ≤ 1024
-v 9 yes yes yes – – yes yes ≤ 10240
-v 10 yes yes yes – – yes yes ∞

Making Requests
This section examines all the possible operations you can do with Kubernetes
resources.

Using kubectl as a Proxy


You must be authenticated to make requests to the Kubernetes API of a cluster,
unless your cluster accepts unauthentified requests, which is unlikely.
A way to run authenticated HTTP requests is to use kubectl as a proxy to make it
deal with the authentication. For this, the kubectl proxy command can be used:

$ kubectl proxy
Starting to serve on 127.0.0.1:8001

On a new terminal, you can now run your HTTP requests without any
authentication. Next, a HOST variable to access the proxy is defined:

$ HOST=http://127.0.0.1:8001

Creating a Resource
You can create a new resource by first creating a Kubernetes manifest describing this
resource—for example, to create a Pod, you can write:

$ cat > pod.yaml <<EOF


apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginx
EOF
You then need to pass the resource description into the body of a POST request
(note that the -X POST flag can be omitted because the --data-binary flag is being
used). For example, to create a pod resource use:

$ curl $HOST/api/v1/namespaces/project1/pods
-H "Content-Type: application/yaml"
--data-binary @pod.yaml

This is equivalent to running the kubectl command:

$ kubectl create --namespace project1 -f pod.yaml -o json

Note that the namespace is not indicated in the pod.yaml file. If you add it, you
must specify the same namespace in the YAML file and in the path, or you will get an
error—that is, the namespace of the provided object does not match the namespace
sent on the request.

Getting Information About a Resource


You can obtain information about a specific resource using a GET request and
passing its name as a parameter (and its namespace if it is a namespaced resource)
in the path. In this example, you will request the information for the pod named
nginx in the project1 namespace:

$ curl -X GET
$HOST/api/v1/namespaces/project1/pods/nginx

This will return information about the resource in the JSON format, using the
kind associated with this resource as a structure; in this example, it is a Pod kind.
This is equivalent to running the kubectl command:

$ kubectl get pods --namespace project1 nginx -o json

Getting the List of Resources


For namespaced resources, you can get the list of resources either cluster-wide or in
a specific namespace. For non-namespaced resources, you can get the list of
resources. In any case, you will use a GET request.

Cluster-wide
To get the list of resources cluster-wide, for namespaced or non-namespaced
resources; for example, for the pod resource, use the following:
$ curl $HOST/api/v1/pods
This will return information about the list of pods in all namespaces, using a
PodList kind. This is equivalent to running the kubectl command:

$ kubectl get pods --all-namespaces -o json

In a Specific namespace
To get the list of resources in a specific namespace, you need to indicate the
namespace in the path; for example, for the pod resource, use this:

$ curl $HOST/api/v1/namespaces/project1/pods

This will return information about the list of pods in the project1 namespace,
using a PodList kind. This is equivalent to running the kubectl command:

$ kubectl get pods --namespace project1 -o json

Filtering the Result of a List


When running a list request, you get as a result the complete list of resources of this
kind, in the specified namespace or cluster-wide, depending on your request.
You may want to filter the result. The most common way to filter resources in
Kubernetes is to use labels. For this, resources need to have defined labels; then,
during a list request, you can define some label selectors. It also is possible to filter
resources based on a limited set of fields by using field selectors.

Using Label Selectors


All Kubernetes resources can define labels. For example, when creating pods, you can
define some labels with kubectl:

$ kubectl run nginx1 --image nginx --labels mylabel=foo


$ kubectl run nginx2 --image nginx --labels mylabel=bar

This results in pods with labels defined in the metadata part of the resource:

$ kubectl get pods nginx1 -o yaml


apiVersion: v1
kind: Pod
metadata:
labels:
mylabel: foo
name: nginx1
[...]

$ kubectl get pods nginx2 -o yaml


apiVersion: v1
kind: Pod
metadata:
labels:
mylabel: bar
name: nginx2
[...]
Now, when running a list request, you can define some label selectors to filter
these resources by using the labelSelector query parameter, which can contain a
comma-separated list of selectors.
Select all the resources defining a specific label, no matter its value; for example,
the mylabel label:

$ curl $HOST/api/v1/namespaces/default/pods?
labelSelector=mylabel

Select all resources not defining a specific label; for example, the mylabel label:

$ curl $HOST/api/v1/namespaces/default/pods?
labelSelector=\!mylabel

Note the exclamation point (!) before the label name—the backslash character (\)
is being used because the exclamation point is a special character for the shell.
Select all resources defining a label with a specific value; for example, mylabel
having the value foo:

$ curl $HOST/api/v1/namespaces/default/pods?
labelSelector=mylabel==foo

or

$ curl $HOST/api/v1/namespaces/default/pods?
labelSelector=mylabel=foo

Select all resources defining a label with a value different from a specific one; for
example, the label mylabel having a value different from foo:

$ curl $HOST/api/v1/namespaces/default/pods?
labelSelector=mylabel\!=foo

Note the exclamation point (!) before the equal sign (=)—the backslash character
(\) is being used because the exclamation point is a special character for the shell.
Select all resources defining a label with a value in a set of values; for example,
the label mylabel having one of the values foo or baz:

$ curl $HOST/api/v1/namespaces/default/pods?
labelSelector=mylabel+in+(foo,baz)

Note the plus characters (+) that encodes spaces in the URL. The original selector
being: mylabel in (foo,baz).
Select all resources defining a label with a value not in a set of values; for
example, the label mylabel having a value different from foo or baz:

$ curl $HOST/api/v1/namespaces/default/pods?
labelSelector=mylabel+notin+(foo,baz)

Note the plus characters (+) that encodes spaces in the URL. The original selector
being: mylabel not in (foo,baz).

You can combine several selectors by separating them with a comma. This will act
as an AND operator. For example, to select all resources with a label mylabel
defined and a label otherlabel being equal to bar, you can use the following label
selector:

$ curl $HOST/api/v1/namespaces/default/pods?
labelSelector=mylabel,otherlabel==bar

Using Field Selectors


You can filter resources using a limited set of fields. For all resources, you can filter
on the metadata.name field; and for all namespaced resources, you can filter on the
metadata.namespace field.
Here is the list of additional fields available for filtering, depending on resources,
for Kubernetes 1.23:
core.event:
involvedObject.apiVersion
involvedObject.fieldPath
involvedObject.kind
involvedObject.name
involvedObject.namespace
involvedObject.resourceVersion
involvedObject.uid
reason
reportingComponent
source
type
core.namespace:
status.phase
core.node:
spec.unschedulable
core.pod:
spec.nodeName
spec.restartPolicy
spec.schedulerName
spec.serviceAccountName
status.nominatedNodeName
status.phase
status.podIP
core.replicationcontroller:
status.replicas
core.secret:
type
apps.replicaset:
status.replicas
batch.job:
status.successful
certificates.certificatesigningrequest:
spec.signerName
Now, when running a list request, you can indicate some field selectors to filter
these resources by using the fieldSelector parameter, which can contain a
comma-separated list of selectors.
Select all resources for which a field has a specific value; for example, the field
status.phase having the value Running:

$ curl $HOST/api/v1/namespaces/default/pods?
fieldSelector=status.phase==Running
or

$ curl $HOST/api/v1/namespaces/default/pods?
fieldSelector=status.phase=Running

Select all resources for which a field has a value different from a specific one; for
example, the field status.phase having a value different from Running:

$ curl $HOST/api/v1/namespaces/default/pods?
fieldSelector=status.phase\!=Running

Note the exclamation point (!) before the equal sign (=)—the backslash character
(\) is being used because the exclamation point is a special character for the shell.

You can combine several selectors by separating them with a comma. This will act
as an AND operator. For example, to select all pods with a phase being equal to
Running and a restart policy not being Always, you can use this field selector:

$ curl $HOST/api/v1/namespaces/default/pods?
fieldSelector=status.phase==Running,
spec.restartPolicy\!=Always

Deleting a Resource
To delete a resource, you need to specify its name (and namespace for namespaced
resources) in the path and use a DELETE request. For example, to delete a pod, use
the following:

$ curl -X DELETE
$HOST/api/v1/namespaces/project1/pods/nginx

This will return the information about the deleted resource in the JSON format,
using the kind associated with the resource—in this case, a Pod kind.
This is equivalent to running the kubectl command (except that you cannot get
information about the deleted resource, only its name with the -o name flag):

$ kubectl delete pods --namespace project1 nginx

Deleting a Collection of Resources


It also is possible to delete a collection of a given resource in a specific namespace,
using a DELETE request; and, for namespaced resources, indicating the namespace
in the path:

$ curl -X DELETE
Another random document with
no related content on Scribd:
Eef “thief’s” my name
I feel no shame;
Eet ees no crime—
Dat rose I got.
Eh! w’at? O! not
Een Juna-time!

—Copyright 1912, by David McKay, and used by kind permission


of author and publisher.

THE SAND STORM


By Lowell Otus Reese

We are thirsty, Pedro mio! and the heat waves leap and beat
Where the Spanish daggers quiver in the mighty desert heat,
And the aching eye looks longing from Old Baldy to the east,
Where the Panamint is crouching like some ugly, hidden beast;
’Tis a hell-wind, Pedro mio! and it beats the sandy hail;
And the Yellow Snake is hissing by the old Mohave trail.

Oh, the loneliness of nature when she turns on you her frown!
When you feel no eye upon you, save the fierce sun glaring down,
Searing death into your body and despair into your soul,
As you reel across the desert with the sky-line for your goal;
When the breath begins to falter and the step begins to fail,
And the Yellow Snake is hissing by the old Mohave trail!

Oh, the awfulness of Nature when she turns on you her frown!
When an unseen hand above you is forever pressing down!
When across the hungry desert flames the scorching sword of
Death,
And the eyes and lips are blackened in the Spirit’s blighting breath!
Oh, the agony of dying, when the step begins to fail,
And the Yellow Snake is hissing by the old Mohave trail!

Oh, the dry and flying sand that stings to fever cheek and brow!
Rain of Hell, O, Pedro mio! and the flame is on us now!
Spiral Phantoms on the desert writhe and wriggle slowly by,
Reaching earthward from the bosom from the black and yellow sky;
Oh, the spiral specters writhing where the yuccas beat and flail,
And the Yellow Snake is hissing by the old Mohave trail!

I have seen it, Pedro mio!—seen it dimly through the wrack!—


Over there beyond the basin where the cloud is whirling black!
Streams of water, peaceful meadows and the shade of bending
trees,
Stirring gently—ah, so gently! in the coolest summer breeze.
Let us turn aside and rest there from the fury of the gale;
For the Yellow Snake is hissing by the old Mohave trail!

Faster—faster, Pedro mio!—for the blood is in my eyes!


I would reach the blessed water ere it o’er my vision dries!
For it thunders in my temples the tumultuous refrain
Of a mountain torrent singing to the first November rain!
Stumble—stumble—onward—farther from the desiccating hail
Where the Yellow Snake is hissing by the old Mohave trail!

We have fallen, Pedro mio! and the vision fair is gone;


But above us and around us yet the tempest hurtles on;
Hark! a swirling raven settles with a flap of twisted wings;
And I seem to feel about us many crawling, creeping things!
We have fallen, Pedro mio! Hark the raging of the gale!
And the Yellow Snake is hissing by the old Mohave trail!

I am dying, Pedro mio! and I fain would go to sleep.


Faugh! the raven ’lights upon me! and the frightened lizards creep
With a rush of tiny claws across my swollen lips! and swift
O’er my breast, a burning blanket, rushing sand-waves eager drift;
We are dying, Pedro mio! in the awful desert gale!
And the Yellow Snake is hissing by the old Mohave trail!

NATHAN HALE
By Francis M. Finch
To drum-beat and heart-beat,
A soldier marches by:
There is color in his cheek,
There is courage in his eye,
Yet to drum-beat and heart-beat
In a moment he must die.

By starlight and moonlight,


He seeks the Briton’s camp;
He hears the rustling flag,
And the arm’d sentry’s tramp;
And the starlight and moonlight
His silent wanderings lamp.

With slow tread and still tread


He scans the tented line;
And he counts the battery guns
By the gaunt and shadowy pine;
And his slow tread and still tread
Gives no warning sign.

The dark wave, the plumed wave,


It meets his eager glance;
And it sparkles ’neath the stars,
Like the glimmer of a lance;—
A dark wave, a plumed wave;
On an emerald expanse.

A sharp clang, a steel clang,


And terror in the sound!
For the sentry, falcon-eyed,
In the camp a spy hath found;
With a sharp clang, a steel clang,
The patriot is bound.

With calm brow, steady brow,


He listens to his doom;
In his look there is no fear,
Nor a shadow-trace of gloom;
But with calm brow and steady brow
He robes him for the tomb.

In the long night, the still night,


He kneels upon the sod;
And the brutal guards withhold
E’en the solemn Word of God!
In the long night, the still night,
He walks where Christ hath trod.

’Neath the blue morn, the sunny morn,


He dies upon the tree;
And he mourns that he can lose
But one life for Liberty;
And in the blue morn, the sunny morn,
His spirit-wings are free.

But his last words, his message-words,


They burn, lest friendly eye
Should read how proud and calm
A patriot could die,
With his last words, his dying words,
A soldier’s battle-cry.

From Fame-leaf and Angel-leaf,


From monument and urn,
The sad of earth, the glad of heaven,
His tragic fate shall learn;
And on Fame-leaf and Angel-leaf
The name of Hale shall burn!

MOTHER AND POET


By Elizabeth Barrett Browning
(Turin, after news from Gaeta, 1861)
Dead! One of them shot in the sea by the east,
And one of them shot in the west by the sea!
Dead! both my boys! when you sit at the feast,
And are wanting a great song for Italy free,
Let none look at me!

Yet I was a poetess only last year,


And good at my art, for a woman, men said;
But this woman, this, who is agonized here,—
The east sea and west sea rhyme on in her head
Forever instead.

What art can a woman be good at? O, vain!


What art is she good at, but hurting her breast
With the milk-teeth of babes, and a smile at the pain?
Ah, boys, how you hurt! you were strong as you pressed,
And I proud, by that test.

What art’s for a woman? to hold on her knees


Both darlings! to feel all their arms round her throat
Cling, strangle a little! to sew by degrees
And ’broider the long-clothes and neat little coat!
To dream and to doat!

To teach them.... It stings there! I made them, indeed,


Speak plain the word country. I taught them, no doubt,
That a country’s a thing men should die for at need.
I prated of liberty, rights, and about
The tyrant cast out.

And when their eyes flashed.... O my beautiful eyes!...


I exulted! nay, let them go forth at the wheels
Of the guns, and denied not.—But then the surprise
When one sits quite alone!—Then one weeps, then one kneels!
God, how the house feels!

At first, happy news came, in gay letters moiled


With my kisses,—of camp-life and glory, and how
They both loved me, and, soon coming home to be spoiled,
In return would fan off every fly from my brow
With their green laurel-bough.

Then was triumph at Turin: “Ancona was free!”


And some one came out of the cheers in the street,
With a face pale as stone, to say something to me,—
My Guido was dead! I fell down at his feet,
While they cheered in the street.

I bore it; friends soothed me; my grief looked sublime


As the ransom of Italy. One boy remained
To be leant on and walked with, recalling the time
When the first grew immortal, while both of us strained
To the height he had gained.

And letters still came, shorter, sadder, more strong,


Writ now but in one hand: “I was not to faint,—
One loved me for two—would be with me ere long:
And ‘Viva l’Italia!’ he died for, our saint,
Who forbids our complaint!”

My Nanni would add, “He was safe, and aware


Of a presence that turned off the balls,—was impressed
It was Guido himself, who knew what I could bear,
And how ’twas impossible, quite dispossessed,
To live on for the rest.”

On which, without pause, up the telegraph-line


Swept smoothly the next news from Gaeta!—Shot.
Tell His Mother. Ah, ah, “his,” “their” mother,—not “mine,”
No voice says “my mother” again to me. What!
You think Guido forgot?

Are souls straight so happy that, dizzy with heaven,


They drop earth’s affections, conceive not of woe?
I think not. Themselves were too lately forgiven
Through that love and sorrow which reconciled so
The above and below.

O Christ of the seven wounds, who look’dst through the dark


To the face of Thy mother! consider, I pray,
How we common mothers stand desolate, mark,
Whose sons, not being Christs, die with eyes turned away,
And no last word to say!

Both boys dead? but that’s out of nature. We all


Have been patriots, yet each house must always keep one.
’Twere imbecile, hewing out roads to a wall;
And, when Italy’s made, for what end is it done,
If we have not a son?

Ah, ah, ah! when Gaeta’s taken, what then?


When the fair wicked queen sits no more at her sport
Of the fire-balls of death crashing souls out of men?
When the guns of Cavalli with final retort
Have cut the game short;

When Venice and Rome keep their new jubilee,


When your flag takes all heaven for its white, green, and red,
When you have your country from mountain to sea,
When King Victor has Italy’s crown on his head
(And I have my dead),—

What then? Do not mock me. Ah, ring your bells low,
And burn your lights faintly! My country is there,
Above the star pricked by the last peak of snow:
My Italy’s THERE,—with my brave civic pair,
To disfranchise despair!

Forgive me. Some women bear children in strength,


And bite back the cry of their pain in self-scorn;
But the birth-pangs of nations will wring us at length
Into wail such as this,—and we sit on forlorn
When the man-child is born.
Dead! One of them shot by the sea in the east,
And one of them shot in the west by the sea!
Both! both my boys! If in keeping the feast,
You want a great song for your Italy free,
Let none look at me!

DORA
With farmer Allan, at the farm, abode William and Dora. William
was his son, and she his niece. He often looked at them and
thought, “I’ll make them man and wife.” Now Dora felt her uncle’s will
in all, and yearned towards William; but the youth, because he had
always been with her in the house, thought not of Dora.
Then there came a day when Allan called his son, and said, “My
son, I married late, but I wish to see my grandchild on my knees
before I die: and I have set my heart upon a match. Now therefore
look to Dora; she is well to look at, thrifty too beyond her age. She is
my brother’s daughter: he and I had once hard words, and parted,
and he died in foreign lands; but for his sake I cared for his daughter
Dora: take her for your wife; for I have wished this marriage, night
and day, for many years.”
But William answered short: “I cannot marry Dora; by my life, I will
not marry Dora.”
Then the old man was wroth, and doubled up his hands, and said:
“You will not, boy! You dare to answer thus? But in my time a father’s
word was law, and so it shall be now for you. Look to it; consider,
William: take a month to think, and let me have an answer to my
wish, or by the Lord that made me, you shall pack, and never more
darken my doors again.”
But William answered madly; bit his lips, and broke away. The
more he looked at her the less he liked her; and his ways were
harsh; but Dora bore them meekly. Then, before the month was out
he left his father’s house, and hired himself to work within the fields;
and, half in love, half spite, he wooed and wed a laborer’s daughter,
Mary Morrison.
When the wedding bells were ringing, Allan called his niece and
said: “My girl, I love you well; but if you speak with him who was my
son, or change a word with her he calls his wife, my home is none of
yours. My will is law.” And Dora promised, being meek. She thought,
“It cannot be: my uncle’s mind will change!” And days went on, and
there was born a boy to William; then distresses came on him, and
day by day he passed his father’s gate, heart-broken, and his father
helped him not. But Dora stored what little she could save, and sent
it them by stealth, nor did they know who sent it; till at last a fever
seized on William, and in harvest time he died. Then Dora went to
Mary. Mary sat and looked with tears upon her boy, and thought hard
things of Dora. Dora came and said: “I have obeyed my uncle until
now, and I have sinned, for it was all through me this evil came on
William at the first. But, Mary, for the sake of him that’s gone, and for
your sake, the woman that he chose, and for this orphan, I am come
to you. You know there has not been for these five years so full a
harvest: let me take the boy, and I will set him in my uncle’s eye
among the wheat; that when his heart is glad of the full harvest, he
may see the boy, and bless him for the sake of him that’s gone.” And
Dora took the child and went her way across the wheat, and sat
upon a mound that was unsown, where many poppies grew. Far off
the farmer came into the field and spied her not; for none of all his
men dare tell him Dora waited with the child. And Dora would have
risen and gone to him, but her heart failed her; and the reapers
reaped, and the sun fell, and all the land was dark.
But when the morrow came, she rose and took the child once
more, and sat upon the mound; and made a little wreath of all the
flowers that grew about, and tied it round his hat to make him
pleasing in her uncle’s eye. Then when the farmer passed into the
field he spied her, and he left his men at work, and came and said:
“Where were you yesterday? Whose child is that? What are you
doing here?”
So Dora cast her eyes upon the ground, and answered softly,
“This is William’s child!”
“And did I not,” said Allan, “did I not forbid you, Dora?”
Dora said again: “Do with me as you will, but take the child, and
bless him for the sake of him that’s gone!”
And Allan said, “I see it is a trick got up betwixt you and the
woman there. I must be taught my duty, and by you! You knew my
word was law, and yet you dared to slight it. Well!—for I will take the
boy; but go you hence, and never see me more.”
So saying he took the boy that cried aloud and struggled hard. The
wreath of flowers fell at Dora’s feet. She bowed over her hands, and
the boy’s cry came to her from the field, more and more distant. She
bowed down her head, remembering the day when first she came,
and all the things that had been. She bowed down and wept in
secret; and the reapers reaped, and the sun fell, and all the land was
dark. Then Dora went to Mary’s house, and stood upon the
threshold. Mary saw the boy was not with Dora. She broke out in
praise to God, that helped her in her widowhood. And Dora said, “My
uncle took the boy; but, Mary, let me live and work with you; he says
that he will never see me more.”
Then answered Mary, “This shall never be, that thou shouldst take
my trouble on thyself: and, now I think, he shall not have the boy, for
he will teach him hardness, and to slight his mother; therefore thou
and I will go, and I will have my boy, and bring him home; and I will
beg of him to take thee back: but if he will not take thee back again,
then thou and I will live within one house, and work for William’s
child, until he grows of age to help us.”
So the women kissed each other, and set out, and reached the
farm. The door was off the latch: they peeped and saw the boy set
up betwixt his grandsire’s knees, who thrust him in the hollows of his
arms, and clapt him on the hands and on the cheeks, like one that
loved him: and the lad stretched out and babbled for the golden seal
that hung from Allan’s watch, and sparkled by the fire. Then they
came in: but when the boy beheld his mother, he cried out to come
to her: and Allan set him down, and Mary said: “O Father!—if you let
me call you so—I never came a-begging for myself, or William, or
this child; but now I come for Dora: take her back, she loves you
well. O Sir, when William died, he died at peace with all men; for I
asked him, and he said he could not ever rue his marrying me—I
had been a patient wife: but, Sir, he said that he was wrong to cross
his father thus: ‘God bless him!’ he said, ‘and may he never know the
troubles I have gone through!’ Then he turned his face and passed—
unhappy that I am! But now, Sir, let me have my boy, for you will
make him hard, and he will learn to slight his father’s memory; and
take Dora back, and let all be as it was before.”
So Mary said, and Dora hid her face by Mary. There was silence in
the room; and all at once the old man burst in sobs: “I have been to
blame—to blame. I have killed my son. I have killed him—but I loved
him—my dear son. May God forgive me! I have been to blame. Kiss
me, my children.”
Then they clung about the old man’s neck, and they kissed him
many times. And Allan was broken with remorse; and all his love
came back a hundred-fold; and for three hours he sobbed o’er
William’s child thinking of William. So those four abode in one house
together; and as years went forward, Mary took another mate; but
Dora lived unmarried until her death.

THE FAMINE[13]
By Henry Wadsworth Longfellow

O the long and dreary Winter!


O the cold and cruel Winter!
Ever thicker, thicker, thicker,
Froze the ice on lake and river,
Ever deeper, deeper, deeper
Fell the snow o’er all the landscape,
Fell the covering snow, and drifted
Through the forest, round the village.
Hardly from his buried wigwam
Could the hunter force a passage;
With his mittens and his snow-shoes
Vainly walked he through the forest,
Sought for bird or beast and found none,
Saw no track of deer or rabbit,
In the snow beheld no footprints,
In the ghastly, gleaming forest
Fell, and could not rise from weakness,
Perished there from cold and hunger.
O the famine and the fever!
O the wasting of the famine!
O the blasting of the fever!
O the wailing of the children!
O the anguish of the women!
All the earth was sick and famished;
Hungry was the air around them,
Hungry was the sky above them,
And the hungry stars in heaven
Like the eyes of wolves glared at them!
Into Hiawatha’s wigwam
Came two other guests, as silent
As the ghosts were, and as gloomy;
Waited not to be invited,
Did not parley at the doorway,
Sat there without word of welcome
In the seat of Laughing Water;
Looked with haggard eyes and hollow
At the face of Laughing Water.
And the foremost said: “Behold me!
I am Famine, Bukadawin!”
And the other said: “Behold me!
I am Fever, Ahkosewin!”
And the lovely Minnehaha
Shuddered as they looked upon her,
Shuddered at the words they uttered,
Lay down on her bed in silence,
Hid her face, but made no answer;
Lay there trembling, freezing, burning
At the looks they cast upon her,
At the fearful words they uttered.
Forth into the empty forest
Rushed the maddened Hiawatha;
In his heart was deadly sorrow,
In his face a stony firmness;
On his brow the sweat of anguish
Started, but it froze and fell not.
Wrapped in furs and armed for hunting,
With his mighty bow of ash-tree,
With his quiver full of arrows,
With his mittens, Minjekahwun,
Into the vast and vacant forest
On his snow-shoes strode he forward.
“Gitche Manito, the Mighty!”
Cried he with his face uplifted
In that bitter hour of anguish,
“Give your children food, O Father!
Give us food, or we must perish!
Give me food for Minnehaha,
For my dying Minnehaha!”
Through the far-resounding forest,
Through the forest vast and vacant
Rang that cry of desolation,
But there came no other answer
Than the echo of his crying,
Than the echo of the woodlands,
“Minnehaha! Minnehaha!”
All day long roved Hiawatha
In that melancholy forest,
Through the shadow of whose thickets,
In the pleasant days of Summer,
Of that ne’er forgotten Summer,
He had brought his young wife homeward
From the land of the Dacotahs;
When the birds sang in the thickets,
And the streamlets laughed and glistened,
And the air was full of fragrance,
And the lovely Laughing Water
Said with voice that did not tremble,
“I will follow you, my husband!”
In the wigwam with Nokomis,
With those gloomy guests, that watched her,
With the Famine and the Fever,
She was lying, the Beloved,
She, the dying Minnehaha.
“Hark!” she said; “I hear a rushing,
Hear a roaring and a rushing,
Hear the Falls of Minnehaha
Calling to me from a distance!”
“No, my child!” said old Nokomis,
“’Tis the night-wind in the pine-trees!”
“Look!” she said; “I see my father
Standing lonely at his doorway,
Beckoning to me from his wigwam
In the land of the Dacotahs!”
“No, my child!” said old Nokomis,
“’Tis the smoke that waves and beckons!”
“Ah!” said she, “the eyes of Pauguk
Glare upon me in the darkness,
I can feel his icy fingers
Clasping mine amid the darkness!
Hiawatha! Hiawatha!”
And the desolate Hiawatha,
Far away amid the forest,
Miles away among the mountains,
Heard that sudden cry of anguish,
Heard the voice of Minnehaha
Calling to him in the darkness,
“Hiawatha! Hiawatha!”
Over snow-fields waste and pathless,
Under snow-encumbered branches,
Homeward hurried Hiawatha,
Empty-handed, heavy-hearted,
Heard Nokomis moaning, wailing:
“Wahonowin! Wahonowin!
Would that I had perished for you,
Would that I were dead as you are!
Wahonowin! Wahonowin!”
And he rushed into the wigwam,
Saw the old Nokomis slowly
Rocking to and fro and moaning,
Saw his lovely Minnehaha
Lying dead and cold before him,
And his bursting heart within him
Uttered such a cry of anguish,
That the forest moaned and shuddered,
That the very stars in heaven
Shook and trembled with his anguish.
Then he sat down, still and speechless,
On the bed of Minnehaha,
At the feet of Laughing Water,
At those willing feet, that never
More would lightly run to meet him,
Never more would lightly follow.
With both hands his face he covered,
Seven long days and nights he sat there,
As if in a swoon he sat there,
Speechless, motionless, unconscious
Of the daylight or the darkness.
Then they buried Minnehaha;
In the snow a grave they made her,
In the forest deep and darksome,
Underneath the moaning hemlocks;
Clothed her in her richest garments,
Wrapped her in her robes of ermine;
Covered her with snow, like ermine;
Thus they buried Minnehaha.
And at night a fire was lighted,
On her grave four times was kindled,
For her soul upon its journey
To the Islands of the Blessed.
From his doorway Hiawatha
Saw it burning in the forest,
Lighting up the gloomy hemlocks;
From his sleepless bed uprising,
From the bed of Minnehaha,
Stood and watched it at the doorway,
That it might not be extinguished,
Might not leave her in the darkness.
“Farewell,” said he, “Minnehaha!
Farewell, O my Laughing Water!
All my heart is buried with you,
All my thoughts go onward with you!
Come not back again to labor,
Come not back again to suffer,
Where the Famine and the Fever
Wear the heart and waste the body.
Soon my task will be completed,
Soon your footsteps I shall follow
To the Islands of the Blessed,
To the Kingdom of Ponemah,
To the Land of the Hereafter!”

THE CHILDREN OF THE BATTLE-FIELD


By James Gowdy Clark

Upon the field of Gettysburg


The summer sun was high,
When Freedom met her haughty foe
Beneath a northern sky.
Among the heroes of the North
That swelled her grand array,
And rushed like mountain eagles forth
From happy homes away,
There stood a man of humble fame,—
A sire of children three,—
And gazed within a little frame
His pictured ones to see:
And blame him not if, in the strife,
He breathed a soldier’s prayer,—
“O Father! shield the soldier’s wife,
And for his children care.”

Upon the field of Gettysburg,


When morning shone again,
The crimson cloud of battle burst
In streams of fiery rain:
Our legions quelled the awful flood
Of shot and steel and shell,
While banners, marked with ball and blood,
Around them rose and fell:
And none more nobly won the name
Of Champion of the Free
Than he who pressed the little frame
That held his children three;
And none were braver in the strife
Than he who breathed the prayer,—
“O Father! shield the soldier’s wife,
And for his children care.”

Upon the field of Gettysburg


The full moon slowly rose,—
She looked, and saw ten thousand brows
All pale in death’s repose;
And down beside a silver stream,
From other forms away,
Calm as a warrior in a dream,
Our fallen comrade lay;
His limbs were cold, his sightless eyes
Were fixed upon the three
Sweet stars that rose in memory’s skies
To light him o’er death’s sea.
Then honored be the soldier’s life,
And hallowed be his prayer,—
“O Father! shield the soldier’s wife,
And for his children care.”
PRESIDENT LINCOLN’S FUNERAL
By Sarah E. Carmichael

Toll! Toll!
Toll! Toll!
All rivers seaward wend.
Toll! Toll!
Toll! Toll!
Weep for the nation’s friend.

Every home and hall was shrouded,


Every thoroughfare was still;
Every brow was darkly clouded,
Every heart was faint and chill.
Oh! the inky drop of poison
In our bitter draught of grief!
Oh! the sorrow of a nation
Mourning for its murdered chief!

Toll! Toll!
Toll! Toll!
Bound in the reaper’s sheaf—
Toll! Toll!
Toll! Toll!
All mortal life is brief.
Toll! Toll!
Toll! Toll!
Weep for the nation’s chief!

Bands of mourning draped the homestead,


And the sacred house of prayer;
Mourning folds lay black and heavy
On true bosoms everywhere:
Yet there were no tear-drops streaming
From the deep and solemn eye
Of the hour that mutely waited
Till the funeral train went by.
Oh! there is a woe that crushes
All expression with its weight!
There is pain that numbs and hushes
Feeling’s sense, it is so great.

Strongest arms were closely folded,


Most impassioned lips at rest;
Scarcely seemed a heaving motion
In the nation’s wounded breast;
Tears were frozen in their sources,
Blushes burned themselves away;
Language bled through broken heart-threads,
Lips had nothing left to say.
Yet there was a marble sorrow
In each still face, chiseled deep;
Something more than words could utter,
Something more than tears could weep.

Selfishly the nation mourned him,


Mourned its chieftain and its friend;
Eye no traitor mist could darken,
Arm no traitor power could bend;
Heart that gathered the true pulses
Of the land’s indignant veins,
And, with their tempestuous spurning,
Broke the slave’s tear-rusted chains:
Heart that tied its iron fibers
Round the Union’s starry band;
Martyr’s heart, that upward beating,
Broke on hate’s assassin hand!
Oh! the land he loved will miss him,
Miss him in its hour of need!
Mourns the nation for the nation
Till its tear-drops inward bleed.

There is one whose life will mourn him,


With a deep, unselfish woe;
One who owned him chief and master
Ere the nation named him so.
That the land he loved will miss him
Does she either think or care?
No! the chieftain’s heart is shrouded,
And her woman’s world was there.
No! the nation was her rival;
Let its glory shine or dim,
He hath perished on its altar—
What were many such to him?

Toll! Toll!
Toll! Toll!
Never again—no more—
Comes back to earth the life that goes
Hence to the Eden shore!

Let him rest!—it is not often


That his soul hath known repose;
Let him rest!—they rest but seldom
Whose successes challenge foes.
He was weary—worn with watching;
His life-crown of power hath pressed
Oft on temples sadly aching—
He was weary, let him rest.

Toll, bells at the Capital!


Bells of the land, toll!
Sob out your grief with brazen lungs—
Toll! Toll! Toll!

THE FISHERMAN’S STORY


By Herbert Bashford

I knew he was morose that day


Because he did not speak to me,

You might also like