Author Archives: Yuriy

Writing first Mastodon bot

Over the years I’ve written quite a few Twitter bots. But since Elon Musk took over – the bird site has become unbearable, so I, like many others, migrated to Mastodon. What Mastodon is, and how it operates is a whole another story, but for our intents and purposes it is similar to Twitter: there is a home timeline where posts from people you follow appear, and you can post to the timeline as well.

Back on Twitter I used to have a bot that would tweet one-liners from Pink Floyd lyrics every hour. Follow me along as I recreate it on Mastodon.

First and foremost you have to make sure the Mastodon instance you’re on allows bots. Some do, some don’t – read the server rules to find out. I am using botsin.space instance that is specifically meant to host bots. Continue reading →

Conditionally ignore terraform resource update

Let’s say you have following SSM parameter resource

resource aws_ssm_parameter private_key {
  name      = var.name
  type      = "SecureString"
  value     = var.key
  overwrite = true
  tags      = var.tags
}

The value of var.key variable changes every time terraform runs. But you need to be able to prevent value update based on some conditions (say, bool variable var.overwrite_old_value).

You can’t use overwrite = property, because if it’s set to false terraform will throw an exception attempting to overwrite the value.

You can’t use lifecycle { ignore_chanes = [...] } because it requires static attribute values and doesn’t accept variables, functions etc.

So how do you update the value only the condition is met? Continue reading →

Dynamic AWS provider in terraform

Recently I needed to create a backup vault resource in Cape Town region, but only if the region is enabled in the AWS account. Straight approach:

provider "aws" {
   region = "af-south-1"
   alias  = "af-south-1"
}

resource "aws_backup_vault" "af_south_1" {
   provider = aws.af-south-1
   name     = "default"
}

would throw exception if af-south-1 region is not enabled for the account. Terraform has the ability to create a resource only if certain condition is met (via count = meta property), but it cannot conditionally declare providers.

But we can conditionally redirect the provider. Continue reading →

Nested Loops in Terraform: Create a map from 2 lists

Recently I encountered a Terraform task in which I had a list of roles and a list of policies and I needed to create a AWS resource for every combination of role-policy. In a “regular” programming language this would be a simple nested loop. Thankfully Terraform 0.12 added for_each and for attributes to declare recurring resources. But two problems remained:

1. I needed some kind of way to nest these for declarations
2. for_each attributes requires a map with a unique key

So let’s tackle these problems one at a time. Let’s we have 2 lists:

locals {
   ROLES = ["developer", "analyst", "manager"]
   POLICIES = ["arn:1", "arn:2", "arn:3"]
}

Continue reading →

Dynamic LINQ to XML

Language Integrated Query (LINQ) is a cool feature of .NET languages like C# that allows you to perform SQL-like query right within the language against language’s data structures (lists, arrays etc.) But one drawback of LINQ – you have to know in advance, at compile time which fields to select, what filter conditions would be. Sometimes there’s a need to supply these at runtime – e.g. user selects which fields they want to see

Thankfully there exists Dynamic LINQ Library that allows you to supply LINQ parameters as a string akin Dynamic SQL. Here’s an example of such query from the library’s homepage:

var query = db.Customers
    .Where("City == @0 and Orders.Count >= @1", "London", 10)
    .OrderBy("CompanyName")
    .Select("new(CompanyName as Name, Phone)");

Now, one thing that LINQ can do is query XML. So in theory if we load, say, this XML:

<DATA_CENTER>
   <SERVER IP="1.2.3.4">
      <OS>Windows</OS>
   </SERVER>
   <SERVER IP="5.6.7.8">
      <OS>Linux</OS>
   </SERVER>
</DATA_CENTER>

into an XElement and run something like this

var query0 = myXElement.Elements()
          .AsQueryable()
          .Select("new (Attribute(\"IP\").Value as IP, Element(\"OS\").Value as OS)")

it would produce list of IPs and OSes. Unfortunately this doesn’t work. Continue reading →

Export Dynamic LINQ to CSV

LINQ allows to perform various queries against different data structures. Wouldn’t it be great if you could easily export result of a LINQ query to CSV? Fortunately you can! This article by Scott Hanselman explain how and culminates in cool in its simplicity code:

namespace FooFoo
{
    public static class LinqToCSV
    {
        public static string ToCsv<T>(this IEnumerable<T> items)
            where T : class
        {
            var csvBuilder = new StringBuilder();
            var properties = typeof(T).GetProperties();
            foreach (T item in items)
            {
                string line = string.Join(",",properties
                      .Select(p => p.GetValue(item, null)
                      .ToCsvValue()).ToArray());
                csvBuilder.AppendLine(line);
            }
            return csvBuilder.ToString();
        }
   
        private static string ToCsvValue<T>(this T item)
        {
            if(item == null) return "\"\"";
   
            if (item is string)
            {
                return string.Format("\"{0}\"", item
                      .ToString().Replace("\"", "\\\""));
            }
            double dummy;
            if (double.TryParse(item.ToString(), out dummy))
            {
                return string.Format("{0}", item);
            }
            return string.Format("\"{0}\"", item);
        }
    }
}

Continue reading →

Keep animated images after uploading to WordPress

Haven’t written in a while. Not that nothing interesting happened, but never got around to. But I finally moved my blogs to AWS Lightsail (very smooth process, by the way) and experienced only one hurdle I wanted to write about (surprisingly, not related to AWS).

After the migration, I noticed that all images on my site lost their animation. When I inspected an image – I found that it’s source goes thru some kind of a proxy “i1.wp.com”. After digging a bit I found that it is used by JetPack site acceleration service – it caches images to serve them faster. Unfortunately cached copies seem to lose some of their properties (like animation).

To fix it – go to JetPack settings and turn “Speed up image load times” off

Jetpack settings

Happy blogging!

Copy/Paste image into an editable DOM element in Chrome

All major browsers allow to paste image directly into a DOM element with contentEditable property set to true. They automatically convert it into IMG element with source pointing to base64 encoded DataURI of the pasted image. That is all browsers, but Chrome. Chrome needs a little help.

In my particular case I need to be able to paste image into an IFRAME with editable body of the content document (for some reason Infragistics WebHtmlEditor ASP.NET control renders itself as this contraption). But the code below applies (with small changes) to any editable DOM element.

To achieve the result we need to perform 3 tasks:

1. Capture image from the clipboard
2. Convert the image to DataURI format
3. Create IMG element with the DataURI source and insert it into the DOM

Take a look at the code below:

if (window.chrome) {
    var elem = document.getElementById("myIframe");
    elem.onload = function () {
        elem.contentWindow.addEventListener(
          "paste", function (event) {
            var me = this;
            var items = (event.clipboardData ||
                event.originalEvent.clipboardData).items;
            var blob = null;
            for (var i = 0; i < items.length; i++) {
                if (items[i].type.indexOf("image") === 0) {
                    blob = items[i].getAsFile();
                }
            }
            if (blob !== null) {
                var reader = new FileReader();
                reader.onload = function (event) {
                    var image = new Image();
                    image.src = event.target.result;
                    image.onload = function () {
                        var range = 
                            me.getSelection().getRangeAt(0);
                        if (range) {
                            range.deleteContents();
                            range.insertNode(image);
                            me.getSelection().removeAllRanges();
                        } else {
                            me.document.body.appendChild(image)
                        }
                    }
                }
                reader.readAsDataURL(blob);
            }
        })
    }
}

Line 1 Checks the browser for chromness (well Edge if you want to pretend to be Chrome – so be it)
Lines 2-3 Grab the IFRAME element and attach onload event handler so we would know when it’s good and ready
Lines 4-8 Attach onpaste event handler and grab clipboard data when the event fires
Lines 10-14 Loop thru clipboard items and if an image is found – read it as blob file
Lines 16-17 Initiate file reader and attach onload event to it so we know when the reading (that begins on line 29) is complete
Lines 18-20 Create a new IMG element, assign DataURI result from file reader as IMG source and attach onload event so we know when the image loading is complete
Lines 21-29 Check if we’re inserting into or replacing any existing data at the target and if so – inserting image into selected range, otherwise simple append it to the target.

And that’s it – with this addition you’re now able to copy/paste images into Chrome in the same way as old respectable browsers do.