Skip to content
This repository has been archived by the owner on Nov 25, 2021. It is now read-only.

Trace cli in go #15

Merged
merged 27 commits into from
Oct 24, 2019
Merged

Trace cli in go #15

merged 27 commits into from
Oct 24, 2019

Conversation

turkenh
Copy link
Member

@turkenh turkenh commented Oct 21, 2019

PR for crossplane/crossplane#744

To do:

  • Test and adapt "Simple Resource Class Selection" Simple Resource Class Selection one pager crossplane#926
  • Better identifier, UID missing if object does not exist
  • Adapt bootstrap.sh to include trace
  • Ready/Pending/Missing states to highlight what is wrong
  • Unit tests
  • Test different scenarios and edge case handling
  • Refactor: better naming, using constants, comments etc
  • Polish graph output

Build & Install:

$ make build
$ make install

Trace a kubernetesapplication:

$ kubectl crossplane trace kubernetesapplication <name> -n <namespace>

Graph output:

# Graphviz needs to be installed first, e.g. brew install graphviz
$  kubectl crossplane trace kubernetesapplication <name> -n <namespace> -o dot |dot -Tpng > /tmp/output.png&& open /tmp/output.png

Copy link
Member

@suskin suskin left a comment

Choose a reason for hiding this comment

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

@turkenh I like that there's an early draft for early review and comments! A couple questions.

Would you be able to use more upbound-style commit messages? Here's the formula I've been using:

category: one-line summary of change

This section down here is longer, and is an explanation
of why the change is being made, or why it is happening
in the way it is being done. The goal of this section is to
help people get into the mindset of the original author, so
that they can keep making changes in the way that the
original author would be, if that's what they want to do.

commit signature

What testing has been done?


check_help "${1}"

crossplane-cli trace "${@}"
Copy link
Member

Choose a reason for hiding this comment

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

Where does crossplane-cli come from?

Copy link
Member Author

Choose a reason for hiding this comment

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

This will be the built binary of go code.
e.g.

go build -o /bin/crossplane-cli cmd/main.go

@@ -0,0 +1,22 @@
#!/usr/bin/env bash
Copy link
Member

Choose a reason for hiding this comment

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

What is the benefit of having a bash wrapper around another binary?

Copy link
Member Author

Choose a reason for hiding this comment

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

As you know, kubectl plugins require an exact name matching the command/subcommand.
This way, we can use a single binary produced from the go code for different subcommands (currently only for trace but later we can consider adding more).

@turkenh
Copy link
Member Author

turkenh commented Oct 21, 2019

Would you be able to use more upbound-style commit messages? Here's the formula I've been using

Sure. I'll most probably, overwrite the commit history here with some sort of "Initial commit" for trace cli go project since the initial commits include experimental changes and I didn't follow well scoped commits.

What testing has been done?

I am mainly testing with wordpress stack example both happy path and also by intentionally breaking some stuff.

First commit adding the golang project for trace cli

Signed-off-by: Hasan Turken <turkenh@gmail.com>
We need the path of the fields for accessing the data in
unstructured object. This commit defines and use some varaibles
for this purpose.

Signed-off-by: Hasan Turken <turkenh@gmail.com>
Now the cred secret used by a provider is also included into the
trace.

Signed-off-by: Hasan Turken <turkenh@gmail.com>
When there are two kinds from different groups, it was not possible
to indicate which one is expected to be traced. e.g. if both
"providers.gcp.crossplane.io" and "providers.aws.crossplane.io",
kubectl get providers simply selects one of them. This commit adds
support for types (or kinds) with group just like kubectl can do.
example:
kubectl crossplane trace providers.gcp.crossplane.io gcp-provider -n gcp

Signed-off-by: Hasan Turken <turkenh@gmail.com>
This commit introduces a new method (with unittests) on trace.crossplane
interface (yet to be consumed by printer) which returns ObjectDetails
struct so that printers can access and print details with their format.
Previously, details of crossplane objects was formatted strings which
means embedding formatting logic into non related objects.

Signed-off-by: Hasan Turken <turkenh@gmail.com>
With this commit, simple printer uses ObjectDetails returned by
crossplane objects and prints details with a common go template.

Signed-off-by: Hasan Turken <turkenh@gmail.com>
Signed-off-by: Hasan Turken <turkenh@gmail.com>
Removes the wrapper bash file which was intented to make future
subcommands available from single binary. Because it is
not necessary right now and we can consider again once we introduce
new subcommands.

Signed-off-by: Hasan Turken <turkenh@gmail.com>
Released artifacts will be platform specific .tar.gz files including
bin directory with all existing bash scripts plus
kubectl-crossplane-trace binary built from go code. ./bootstrap.sh
will fetch platform specific artifact and extract to appropriate
directory as before. For backward compatibility, if RELEASE is set
to master, then bootstrap will behave as before but missing trace.

Signed-off-by: Hasan Turken <turkenh@gmail.com>
Each crossplane object now has an IsReady method indicating its
readiness. This information is also made avilable on simpleprinter
with a sign (ready/notReady/missing) and on graph with colors
(red missing, orange notReady). This could help to immediately
identify what can be wrong.

Signed-off-by: Hasan Turken <turkenh@gmail.com>
Load all auth plugins which makes the tool able to authenticate
with cloud providers.
/~https://github.com/kubernetes/client-go/tree/master/examples#auth-plugins

Signed-off-by: Hasan Turken <turkenh@gmail.com>
Signed-off-by: Hasan Turken <turkenh@gmail.com>
Adds unit tests for fetchObj and BuildGraph in graphbuilder.go
Also includes some related fixing and refactorings.

Signed-off-by: Hasan Turken <turkenh@gmail.com>
Signed-off-by: Hasan Turken <turkenh@gmail.com>
Signed-off-by: Hasan Turken <turkenh@gmail.com>
@turkenh turkenh marked this pull request as ready for review October 23, 2019 13:20
@turkenh turkenh requested a review from suskin October 23, 2019 13:24
@turkenh
Copy link
Member Author

turkenh commented Oct 23, 2019

@suskin I still have something to do, but believe that PR is ready for review.

It would be great if you could have a chance to test on your side as well.

With v0.4 Portable classes being removed and resource classes,
providers and managed resources are being changed as cluster scoped.
This commitadds anticipated compatibility changes by still trying
to keep it backward compatible with v0.3

Signed-off-by: Hasan Turken <turkenh@gmail.com>
Copy link
Member

@suskin suskin left a comment

Choose a reason for hiding this comment

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

Awesome - thanks @turkenh ! Since it's a larger pull request it's a little harder to do an in-depth review quickly, but it looks pretty good to me. I just had some minor questions and suggestions.

I'm going to approve this since we're in different timezones, but I'd like us to at a minimum address the comments about testing (add make test, verify that installation from a release has been tested) and usability before we merge the changes.

For posterity, I asked @turkenh in a separate conversation whether we would be able to use more of the code from kubectl - for example, for the kubectl get commands. He said that he looked, but that he wasn't able to find anything which was easily reusable. We both agreed that we'd like to revisit that in the future at some point, but that we're okay with doing a quick thing for now so we can start using the tool.

@@ -10,13 +10,23 @@ if [[ -z "${RELEASE}" ]]; then
RELEASE=master
fi

PLATFORM="linux"
Copy link
Member

Choose a reason for hiding this comment

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

@turkenh what platform are you running? I'm testing this on darwin.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm also using darwin, but just tested on a debian instance and working fine.

Copy link
Member

Choose a reason for hiding this comment

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

kk

install:
ln -si $(abspath bin/kubectl-crossplane-stack-*) $(INSTALL_DIR)/
ln -si $(abspath bin/kubectl-crossplane-*) $(INSTALL_DIR)/
.PHONY: install

uninstall:
Copy link
Member

Choose a reason for hiding this comment

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

Is the .DS_Store file below needed? Should we remove it and .gitignore it?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, definitely not needed. Deleted and added to .gitignore.

curl -sL -o "${PREFIX}"/bin/kubectl-crossplane-stack-generate_install https://raw.githubusercontent.com/crossplaneio/crossplane-cli/"${RELEASE}"/bin/kubectl-crossplane-stack-generate_install >/dev/null
curl -sL -o "${PREFIX}"/bin/kubectl-crossplane-stack-list https://raw.githubusercontent.com/crossplaneio/crossplane-cli/"${RELEASE}"/bin/kubectl-crossplane-stack-list >/dev/null
else
curl -sL /~https://github.com/crossplaneio/crossplane-cli/releases/download/"${RELEASE}"/crossplane-cli-"${RELEASE}"-"${PLATFORM}".tar.gz | tar xz --strip 1 -C "${PREFIX}"/bin
Copy link
Member

Choose a reason for hiding this comment

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

Has this been tested on linux and darwin?

Copy link
Member Author

@turkenh turkenh Oct 24, 2019

Choose a reason for hiding this comment

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

Yes, works fine on both. Tested with my test releases: /~https://github.com/turkenh/crossplane-cli/releases/tag/v0.1.2-test

We need to follow the naming convention here with our releases.

crossplane-cli-"${RELEASE}"-"${PLATFORM}".tar.gz

bootstrap.sh Outdated
curl -sL -o "${PREFIX}"/bin/kubectl-crossplane-stack-generate_install https://raw.githubusercontent.com/crossplaneio/crossplane-cli/"${RELEASE}"/bin/kubectl-crossplane-stack-generate_install >/dev/null
curl -sL -o "${PREFIX}"/bin/kubectl-crossplane-stack-list https://raw.githubusercontent.com/crossplaneio/crossplane-cli/"${RELEASE}"/bin/kubectl-crossplane-stack-list >/dev/null
if [[ "${RELEASE}" == "master" ]]; then
echo "trace subcommand will not be available from master, RELEASE must be set to a released version."
Copy link
Member

Choose a reason for hiding this comment

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

I have been taking the approach of sending progress and error messages to stderr instead; let's redirect this: >&2

Also, could we put some sort of attention-grabbing prefix at the beginning of the message, and add an example of a released version? For example:

echo "NOTICE: the trace command is not available from master. RELEASE must be set to a released version (such as v0.2.0). See /~https://github.com/crossplaneio/crossplane-cli/releases for the full list of releases." >&2

Copy link
Member Author

Choose a reason for hiding this comment

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

Both makes sense. Done.

bootstrap.sh Outdated
@@ -10,13 +10,23 @@ if [[ -z "${RELEASE}" ]]; then
RELEASE=master
fi

PLATFORM="linux"
if [[ "$OSTYPE" == "darwin"* ]]; then
Copy link
Member

Choose a reason for hiding this comment

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

Style nitpick: could we change this to use curly braces for consistency? So something more like "${OSTYPE}". I like using curly braces for clarity for scripts. In general, I like to use the long forms of syntax in scripts, since they're usually clearer.

)

type Application struct {
u *unstructured.Unstructured
Copy link
Member

Choose a reason for hiding this comment

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

In general I like descriptive variable names over single-letter names, but if this is the go idiom for untyped and anonymous variables like this, I'm okay with it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Go idiom likes short names but not sure this applies here.
Changed it as u -> instance which is already something that I used somewhere else in the code.

return o.GetStatus() == stateAppSubmitted
}

func (o *Application) GetRelated(filterByLabel func(metav1.GroupVersionKind, string, string) ([]unstructured.Unstructured, error)) ([]*unstructured.Unstructured, error) {
Copy link
Member

Choose a reason for hiding this comment

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

Something to keep in mind for the future is that it'd be great if we could add support for more types without having to recompile the trace binary. We don't have to have a solution now, but it's something to think about.

Copy link
Member Author

@turkenh turkenh Oct 24, 2019

Choose a reason for hiding this comment

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

Yes, that's a good point.
This is something that I also considered in the context of making this tool as a "general purpose" tracing tool as you mentioned in the crossplane/crossplane#744. This could be possible with some sort of configuration file which describes the types and their relations with other types.

"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

func TestApplication_GetObjectDetails(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

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

Awesome! Could we also add a make test target for running the unit tests?

}

func (o *Application) GetStatus() string {
return getNestedString(o.u.Object, fieldsStatusState...)
Copy link
Member

Choose a reason for hiding this comment

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

I usually prefer that the imports for shared functionality are explicit so they're easier to follow for readers who are not familiar with the codebase, but this is something we can probably reexamine in the future.

Copy link
Member Author

Choose a reason for hiding this comment

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

Well, if you mean getNestedString this is not coming from an import. it is a helper in the same package.
Otherwise, I agree with your comment in general.

}

// Print prints graph definition in Graphviz dot format for a given root node.
func (p *GraphPrinter) Print(nodes []*Node) error {
Copy link
Member

Choose a reason for hiding this comment

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

Yesss

@suskin
Copy link
Member

suskin commented Oct 24, 2019

I forgot to mention, but I did some integration testing on my side as well. I used it to inspect the statuses for a mysqlinstance and a kubernetesapplication which I created as part of the Stacks guide. And I used it to watch the kubernetesapplication become ready over time. It was way better than doing those things without the tool!

As another note, we'll want to update some of the documentation - in this repo and in crossplane - to recommend that we use trace. The documentation will need to be updated before we consider the trace work to be complete.

Adds .DS_Store (a mac os system file) and produced binary to
.gitignore.

Signed-off-by: Hasan Turken <turkenh@gmail.com>
Attention grabbing message for installations from master and
curly braces for variables.

Signed-off-by: Hasan Turken <turkenh@gmail.com>
Changed as "instance" from "u"

Signed-off-by: Hasan Turken <turkenh@gmail.com>
Signed-off-by: Hasan Turken <turkenh@gmail.com>
@turkenh
Copy link
Member Author

turkenh commented Oct 24, 2019

@suskin Thanks a lot for reviewing and testing!
All of your comments should be resolved right now.

For posterity, I asked @turkenh in a separate conversation whether we would be able to use more of the code from kubectl - for example, for the kubectl get commands. He said that he looked, but that he wasn't able to find anything which was easily reusable. We both agreed that we'd like to revisit that in the future at some point, but that we're okay with doing a quick thing for now so we can start using the tool.

Most related parts from kubectl get was the ones for printing, since for interacting with api-server I used client-go dynamic client which was pretty good. I checked /~https://github.com/kubernetes/cli-runtime which could be helpful, but as @suskin mentioned, I found adding my own simple printer as more easy and flexible. I still agree that this should be something to revisit in future.

I forgot to mention, but I did some integration testing on my side as well. I used it to inspect the statuses for a mysqlinstance and a kubernetesapplication which I created as part of the Stacks guide. And I used it to watch the kubernetesapplication become ready over time. It was way better than doing those things without the tool!

Thanks a lot!
Which cloud provider were you using? I always used GCP and now looking for a way to test with others. Another this that needs to be test is the recent changes happening around simple resource class selection.

Adapt crossplane object to work with both v0.3 and v0.4

Signed-off-by: Hasan Turken <turkenh@gmail.com>
Signed-off-by: Hasan Turken <turkenh@gmail.com>
Previously overview was printed first and details second. But for
long outputs, user first sees the bottom of details as console
slided down. With this commit, overview will be what user sees
first.

Signed-off-by: Hasan Turken <turkenh@gmail.com>
With this change kubeconfig is selected with the same logic as
kubectl.
--kubeconfig flag overwrites all
env KUBECONFIG selected otherwise
default path (~/.kube/config) in homedir is none set

Signed-off-by: Hasan Turken <turkenh@gmail.com>
@suskin
Copy link
Member

suskin commented Oct 24, 2019

Which cloud provider were you using?

I was using GCP, and I was also using Crossplane 0.3 instead of master

@@ -10,13 +10,23 @@ if [[ -z "${RELEASE}" ]]; then
RELEASE=master
fi

PLATFORM="linux"
Copy link
Member

Choose a reason for hiding this comment

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

kk

err = g.fetchObj(root)
if kerrors.IsNotFound(err) {
return root, nil, errors.New(
fmt.Sprintf("Object to trace is not found: \"%s\" \"%s\" in namespace \"%s\"", groupRes, name, namespace))
Copy link
Member

Choose a reason for hiding this comment

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

Grammar nitpick: Object to trace not found or Object to trace was not found would be better. I would normally go with the Object to trace not found form

resource: "kubernetescluster",
},
want: want{
err: errors.NewNotFound(schema.ParseGroupResource("kubernetesclusters.compute.crossplane.io"), "test"),
Copy link
Member

Choose a reason for hiding this comment

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

When I ran the unit tests, I got this:

--- FAIL: TestKubeGraphBuilder_BuildGraph (0.00s)
    --- FAIL: TestKubeGraphBuilder_BuildGraph/NotExist (0.00s)
        graphbuilder_test.go:306: g.BuildGraph(...): -want error, +got error:   interface{}(
            - 	e`kubernetesclusters.compute.crossplane.io "test" not found`,
            + 	e`Object to trace is not found: "kubernetescluster" "test" in namespace "testnamespace"`,
              )

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed. I should get used to running make test before pushing :)

Copy link
Member

Choose a reason for hiding this comment

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

haha : ) - ideally we'll also set up some automated builds so we don't need to always remember

@suskin suskin self-requested a review October 24, 2019 18:35
Previously it was shown as tabbed but on AWS and Azure
resources value does not fit into colums which breaks
layout

Signed-off-by: Hasan Turken <turkenh@gmail.com>
From "Object to trace is not found" to "Object to trace not found"
as per review comments.

Signed-off-by: Hasan Turken <turkenh@gmail.com>
Full object reference from resource class to provider is not available.
Only name of the provider there. To resolve the provider, there was a
workaround in the code, which sets apiVersion of the provider as the
apiVersion of the resource class. This assumption does not hold with
v1beta1 versions recently introduced in stack-gcp. This commit fixes
by setting group instead of apiVersion.

Signed-off-by: Hasan Turken <turkenh@gmail.com>
Copy link
Member

@suskin suskin left a comment

Choose a reason for hiding this comment

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

Thanks @turkenh ! This looks good to me. I believe we'll need to do a couple more things to get a release up, but we can work on it after this pull request is merged.

@suskin suskin self-assigned this Oct 24, 2019
@suskin suskin merged commit 1d93603 into crossplane:master Oct 24, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants